2

This may be a duplicate but the closest I could find was Comparing 2 lists consisting of dictionaries with unique keys in python which did not work for me.

So I have two lists of dictionaries.

y = [{'a': 3, 'b': 4, 'c': 5}, {'a': 1, 'b': 2, 'c': 3}]
y = [{'a': 4, 'b': 5, 'c': 6}, {'a': 1, 'b': 2, 'c': 3}]

How do I compare these two lists so my compare results in the intersection of the two lists. I can't convert it to set since it says unhashable type (dict)

Community
  • 1
  • 1
as3rdaccount
  • 3,711
  • 12
  • 42
  • 62

4 Answers4

5

Your question and it's title seem at odds with each other.

The intersection of the 2 lists would be the common elements of both list. The question title requests the elements that are not in both lists. Which is it that you want?

For the intersection, it is not very efficient (being O(n^2) in time), but this list comprehension will do it:

>>> a = [{'a': 3, 'b': 4, 'c': 5}, {'a': 1, 'b': 2, 'c': 3}]
>>> b = [{'a': 4, 'b': 5, 'c': 6}, {'a': 1, 'b': 2, 'c': 3}]
>>> [d for d in a if d in b]
[{'a': 1, 'b': 2, 'c': 3}]
mhawke
  • 84,695
  • 9
  • 117
  • 138
1
y1 = [{'a': 3, 'b': 4, 'c': 5}, {'a': 1, 'b': 2, 'c': 3}]
y2 = [{'a': 4, 'b': 5, 'c': 6}, {'a': 1, 'b': 2, 'c': 3}]
print [x for x in y1 if x in y2] # prints [{'a': 1, 'c': 3, 'b': 2}]
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
1

A dict (or list) is not hashable, however, a tuple is. You can convert the list of dicts to a set of tuples. Perform the intersection and then convert back

the code to convert to a set-of-tuples

y_tupleset = set(tuple(sorted(d.items())) for d in y)

the code to convert back the intersected set-of-tuples to a list-of-dicts

y_dictlist = [dict(it) for it in list(y_tupleset)]

Thus, the full code would be:

y0 = [{'a': 3, 'b': 4, 'c': 5}, {'a': 1, 'b': 2, 'c': 3}]
y1 = [{'a': 4, 'b': 5, 'c': 6}, {'a': 1, 'b': 2, 'c': 3}]

y0_tupleset = set(tuple(sorted(d.items())) for d in y0)
y1_tupleset = set(tuple(sorted(d.items())) for d in y1)
y_inter = y0_tupleset.intersection(y1_tupleset)
y_inter_dictlist = [dict(it) for it in list(y_inter)]

print(y_inter_dictlist)
# prints the following line
[{'a': 1, 'c': 3, 'b': 2}]

edit: d.items() is valid on python3, for python2, it should be replaced with d.iteritems()

Haleemur Ali
  • 26,718
  • 5
  • 61
  • 85
0

Pick your poison:

y1 = [{'a': 3, 'b': 4, 'c': 5}, {'a': 1, 'b': 2, 'c': 3}]
y2 = [{'a': 4, 'b': 5, 'c': 6}, {'a': 1, 'b': 2, 'c': 3}]
y3 = [{'a': 1, 'b': 2, 'c': 3}, {'a': 4, 'b': 2, 'c': 6}]

# Returns a list of keys that are in both dictionaries
def intersect_keys(d1, d2):
    return [k for k in d1 if k in d2]

# Returns a list of values that are in both dictionaries
def intersect_vals(d1, d2):
    return [v for v in d1.itervalues() if v in d2.itervalues()]

# Returns a list of (key,value) pairs that are in both dictionaries
def intersect_pairs(d1, d2):
    return [(k,v) for (k,v) in d1.iteritems() if k in d2 and d2[k] == v]


print(intersect_keys(*y1))      # ['a', 'c', 'b']
print(intersect_vals(*y1))      # [3]
print(intersect_pairs(*y1))     # []

print(intersect_keys(*y2))      # ['a', 'c', 'b']
print(intersect_vals(*y2))      # []
print(intersect_pairs(*y2))     # []

print(intersect_keys(*y3))      # ['a', 'c', 'b']
print(intersect_vals(*y3))      # [2]
print(intersect_pairs(*y3))     # [('b', 2)]

Note: the examples compare the two elements of the y* list, which was how I interpreted your question. You could of course use something like:

print(intersect_pairs(y1[0], y2[0]))

To compute the intersection the first dictionary in the y1 and y2 lists.

jedwards
  • 29,432
  • 3
  • 65
  • 92