Reputation: 3181
I want to return True of two lists of dictionaries are memberwise equal (member being the content of each dictionary, disregarding order of lists), else return False. What is good way to do that in python?
Eg, if a = [dict1, dict2] and b = [dict2, dict1], then a is equal to b. (dict1's and dict2's keys and values must match.)
Here's my way:
def _compare(self, a,b):
if len(a) != len(b):
return False
for d in a:
if d in b:
continue
else:
return False
return True
Upvotes: 5
Views: 5902
Reputation: 82889
(The question is a bit ambiguous. As I understand it, a
and b
are both lists containing dictionaries, and you want to check whether the two lists contain the same dictionaries, in any order.)
You could just use the all
builtin, although this will be rather slow for long lists, having O(n²):
>>> lst1 = [{1:2, 3:4}, {5:6, 7:8}]
>>> lst2 = [{7:8, 5:6}, {3:4, 1:2}]
>>> len(lst1) == len(lst2) and all(x in lst2 for x in lst1)
True
Better convert the list
of dict
to set
of frozenset
holding the dicts items,having O(n):
>>> set_of_tuples = lambda l: set(frozenset(d.items()) for d in l)
>>> set_of_tuples(lst1)
set([frozenset([(1, 2), (3, 4)]), frozenset([(5, 6), (7, 8)])])
>>> set_of_tuples(lst1) == set_of_tuples(lst2)
True
Addendum: This requires that the values in the dicts are hashable. Also, it assumes no duplicate dicts within the same list (but in this case you code would not work, either, so I think this assumption is valid). If there are duplicates, use Counter
as shown in another answer.
Upvotes: 4
Reputation: 106455
You can convert the dicts in the lists to tuples of items first and then use collections.Counter
to compare the two lists regardless of order:
from collections import Counter
def compare(a, b):
return Counter(tuple(d.items()) for d in a) == Counter(tuple(d.items()) for d in b)
so that the comparison of the following two lists of dicts that are memberwise-equal:
compare(
[{1: 2, 2: 3}, {3: 4, 5: 6}, {3: 4, 5: 6}],
[{3: 4, 5: 6}, {1: 2, 2: 3}, {3: 4, 5: 6}]
)
returns True
.
Upvotes: 1
Reputation: 12895
One way is to compare the set of values:
da = { 1:'a', 2:'b', 3:'c'}
db = { 3:'a', 1:'b', 2:'c'}
set(db.values()) == set(da.values())
Upvotes: 0
Reputation: 21433
This is for comparing members nicely:
a.keys() == b.keys()
keys()
returns a view over the members of a dictionary.
To make this work with 2 lists of dicts, just wrap it in a comprehension:
[a.keys() == b.keys() for a, b in zip(list1, list2)]
It will return a list of True's and False's.
If you want to know it they are all equal, then just wrap it with all()
, so the final result is:
all([a.keys() == b.keys() for a, b in zip(list1, list2)])
a = {}
b = {}
for i in range(100_0000):
a[i] = i
b[i] = i
In [10]: %timeit a==b # wrong, but just for comparison
11.6 ms ± 265 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [11]: %timeit a.keys()==b.keys()
16.9 ms ± 209 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [17]: %timeit _compare(a, b)
30.6 ms ± 633 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [18]: %timeit set(a.keys()) == set(b.keys())
71 ms ± 1.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Upvotes: 1