Skip to content Skip to sidebar Skip to footer

Create A Dictionary Of Non-contradicting Items From A List Of Dictionaries

This question is inspired by this question. I'd like to get a dictionary from a list of dictionaries that should contain all key/value pairs from all dictionaries that are either o

Solution 1:

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

data = {}
for d in dicts:
    for k, v in d.iteritems():
        data.setdefault(k, set()).add(v)
out = dict((k, v.pop()) for k, v in data.iteritems() if len(v) == 1)

# out == {'a': 3, 'd': 2}

… or a one-liner:

import itertools as it

dict((k, v.pop()[1]) for k,v in ((k, set(v)) for k, v in it.groupby(sorted(it.chain(*(d.iteritems() for d in dicts))), key=lambda x: x[0])) if len(v) == 1)

Solution 2:

Yours is pretty close to as elegant as I can think of. The only change I would make is to replaced the nested for loop with a itertools.chain()'ed iterator, like this:

import collections

def dict_intersection(dicts):
        c=collections.defaultdict(set)
        for k,v in itertools.chain(*[d.iteritems() for d in dicts]):
                c[k].add(v)
        return {a: next(iter(b)) for a, b in c.iteritems() if len(b) == 1}

Edit(1): The below code answers a slightly different question - how to get any entry which appears with the same key and value in at least two of the input dictionaries.

My answer from the comments in the other question:

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

This is nominally a "one-liner" but I've spread it over multiple lines to (hopefully) make it a bit clearer.

The way it works is (starting from the inside and working out):

  • Use itertools.chain() to get an iterator over the elements of all the dictionaries.
  • Use collections.Counter() to count how many times each key, value pair appears in the dictionaries.
  • Use a list comprehension to filter the Counter for those key, value pairs occurring at least twice.
  • Convert the list back into a dict.

Solution 3:

All solutions so far assume that all dictionary values are hashable. Since the code won't get slower and only little more complex without this assumption, I'd drop it. Here's a version that works for all values that support !=:

def dict_intersection(dicts):
    result = {}
    conflicting = set()
    for d in dicts:
        for k, v in d.iteritems():
            if k not in conflicting and result.setdefault(k, v) != v:
                del result[k]
                conflicting.add(k)
    return result

The set conflicting will only contain dictionary keys, which will always be hashable.


Solution 4:

To get the intersection:

dict(reduce(lambda x, y: x & y, map(set, map(lambda x: x.iteritems(), dicts))))

Of course, this drops unique values, so we need to get the complement:

dict(reduce(lambda x, y: x - y, map(set, map(lambda x: x.iteritems(), dicts))))

Combining the resulting dictionaries gives us the result set:

def dict_intersection(d):
    x = dict(reduce(lambda x, y: x & y, map(set, map(lambda x: x.iteritems(), dicts))))
    y = dict(reduce(lambda x, y: x - y, map(set, map(lambda x: x.iteritems(), dicts))))
    return dict(x.items() + y.items())

If my set fu was stronger I could get it down to a one liner, but not today it seems.


Post a Comment for "Create A Dictionary Of Non-contradicting Items From A List Of Dictionaries"