user1008636
user1008636

Reputation: 3181

Comparing 2 lists of dictionaries

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

Answers (4)

tobias_k
tobias_k

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

blhsing
blhsing

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

gregory
gregory

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

PascalVKooten
PascalVKooten

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)])

Benchmarks

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

Related Questions