Подтвердить что ты не робот

Python: найти только общие пары ключ-значение нескольких dicts: dict intersection

У меня есть 0 или более dicts в списке:

>>> dicts = [dict(a=3, b=89, d=2), dict(a=3, b=89, c=99), dict(a=3, b=42, c=33)]

Я хочу создать новый dict, который содержит только те ключи, которые находятся в all приведенных выше dicts, и только если значения одинаковы:

>>> dict_intersection(*dicts)
{"a": 3}

Я чувствую, что должен быть элегантный способ писать dict_intersection, но я просто придумываю неэлегантные и/или неэффективные решения. Предложения?

4b9b3361

Ответ 1

>>> dict(set.intersection(*(set(d.iteritems()) for d in dicts)))
{'a': 3}

Примечание. Это решение требует, чтобы словарные значения были хешируемыми, в дополнение к клавишам.

Ответ 2

Поскольку пары ключ/значение уже должны быть в первом dict, вы можете перебирать эти элементы dict.

dict(pair for pair in dicts[0].items() 
     if all((pair in d.items() for d in dicts[1:])))

Выглядит менее элегантно, чем ответ на interjay, но работает без ограничения хешируемых значений.

Изменить: Изменено выражение all выражению генератора для повышения скорости

Ответ 3

>>> dicts
[{'a': 3, 'b': 89, 'd': 2}, {'a': 3, 'c': 99, 'b': 89}, {'a': 3, 'c': 33, 'b': 42}]
>>> sets = (set(d.iteritems()) for d in dicts)
>>> dict_intersection = dict(set.intersection(*sets))
>>> dict_intersection
{'a': 3}

Ответ 4

Как это?

def intersect_two_dicts (d1, d2):
    return { k:v for k,v in d1.iteritems() if ((k in d2)and(d1[k]==d2[k])) }

def intersect_dicts (list_of_dicts):
    return reduce(intersect_two_dicts, list_of_dicts)

# Tests
dicts = [dict(a=3, b=89, d=2), dict(a=3, b=89, c=99), dict(a=3, b=42, c=33)]
print (intersect_two_dicts(dicts[0], dicts[1]))
print (intersect_dicts(dicts))

Изменить (1): Я не уверен, какой из них самый быстрый. Решения set.intersection, безусловно, самые изящные (короткие лайнеры!), Но мне было бы интересно увидеть некоторый бенчмаркинг.

Изменить (2): Бонус - получить любые словарные записи, чьи пары (ключ: значение) являются общими для любых двух словарей:

{k:count for k,count in
 collections.Counter(itertools.chain(*[d.iteritems() for d in dicts])).iteritems()
 if count > 1}

Ответ 5

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