Python разбивает список словарей на два списка на основе условных - PullRequest
0 голосов
/ 22 июня 2019

Я пытаюсь разделить список словарей на два списка, основываясь на условном типе значения в словарях. Есть ли способ сделать это в понимании однопроходного списка?

В настоящее время я делаю это:

nonvals = [{k: v for k, v in act.items() if type(v) != int} for act in actual]
vals = [{k: v for k, v in act.items() if type(v) == int} for act in actual]

Можно ли в любом случае вернуть два списка на основе условного понимания одного списка? Если нет, то какой более питонический способ разбить этот список словарей?

Я пытаюсь сделать следующее в качестве окончательного решения, но я чувствую, что существует много повторений кода и посторонних циклов, которых можно избежать - для справки actual и expected являются списками словарей. Я просто хотел бы получить окончательное решение в кратчайшие сроки.

Я хочу проверить, не являются ли ключи не-int, пары значений в ожидаемом ключе не-int, пары значений в фактическом. И на самом деле я просто хочу проверить, все ли пары ключ-значение int находятся в диапазоне [-11, 11].


expected = [{'time': '12:34:22', 'place': 'LA', 'person': 'Mike', 'val1': 2, 'val2': 3, 'val3': 4},
            {'time': '11:45:15', 'place': 'SF', 'person': 'Emily', 'val1': 2, 'val2': 3, 'val3': 4}]

actual = [{'time': '12:34:22', 'place': 'LA', 'person': 'Mike', 'val1': 2, 'val2': 3, 'val3': 4},
          {'time': '11:45:15', 'place': 'SF', 'person': 'Emily', 'val1': 2, 'val2': 3, 'val3': 4},
          {'time': '21:19:57', 'place': 'LA', 'person': 'Leo', 'val1': 2, 'val2': 3, 'val3': 4},
          {'time': '15:43:11', 'place': 'LA', 'person': 'Marge', 'val1': 2, 'val2': 3, 'val3': 4}]

def check(expected, actual):

    nonvals = [{k: v for k, v in act.items() if type(v) != int} for act in actual]
    vals = [{k: v for k, v in act.items() if type(v) == int} for act in actual]


    for act in actual:
       for k, v in act.items():
           if v in vals and v not in range(-11, 11):
                 return False

    for exp in expected:
       if {k: v for k, v in exp.items() if type(v) != int} not in nonvals:
           return False

    return True

1 Ответ

1 голос
/ 22 июня 2019

Не существует общего питонического решения, которое бы разделяло список на основе некоторого условия, не говоря уже о том, чтобы выполнять еще более сложную работу по разбиению словаря на основе некоторого условия (например, типа значения, соответствующего определенному типу).

Выполнение того, что вы делаете, является довольно читабельным и неплохим решением, но если вы столкнетесь с этой проблемой чаще, вы можете просто написать функцию, которую затем сможете применить к словарям в списке:

def pdict(d, condition):
    """
    Partition a dictionary based on some condition function
    :param d: a dict
    :param condition: a function with parameters k, v returning a bool for k: v in d
    :return: two dictionaries, with the contents of d, split according to condition
    """
    return {
        k: v for k, v in d.items() if condition(k, v)
    }, {
        k: v for k, v in d.items() if not condition(k, v)
    }  


original = [{'a': 1, 'b': 'two', 'c': 3}, {'a': 'one', 'b': 2}, {'a': 1, 'b': 2}]
int_result, nonint_result = [
    list(t) for t in zip(*[
        pdict(d, lambda k, v: isinstance(v, int)) for d in original
    ])   
]

print(int_result)
print(nonint_result)

Это чисто и позволяет просто повторно использовать partition_dict для аналогичных случаев.

Вывод, полученный в примере:

[{'a': 1, 'c': 3}, {'b': 2}, {'a': 1, 'b': 2}]
[{'b': 'two'}, {'a': 'one'}, {}]

Более простой пример) используя pdict():

d = {1: 42, 2: 33, 3: 5, 4: 10}
odd, even = pdict(d, lambda k, v: v % 2 == 1)
print(odd, even)

Вывод:

{2: 33, 3: 5} {1: 42, 4: 10}

Перезапись pdict(), которая повторяется только один раз (как предложено @blues), но немного сложнее:

def pdict(d, condition):
    """
    Partition a dictionary based on some condition function
    :param d: a dict
    :param condition: a function with parameters k, v returning a bool for k: v in d
    :return: two dictionaries, with the contents of d, split according to condition
    """
    meets_condition = {}
    does_not_meet_condition = {}
    for k, v in d.items():
        if condition(k, v):
            meets_condition[k] = v
        else:
            does_not_meet_condition[k] = v
    return meets_condition, does_not_meet_condition

Недостатком этого может быть то, что для каждого элемента, добавляемого в словари, могут быть некоторые издержки при поиске, которых исходный пример кода может избежать при понимании словаря.Вы можете запустить тесты производительности, чтобы решить, что является предпочтительным, но последнее решение избегает повторения всего исходного словаря дважды.Я думаю, что предпочел бы первое решение для всего, что не слишком много.

...