0

I have two dictionaries with a list in the value part. Something like this

dict1 = {red:[1,2,3], blue:[7,6,9], green:[3,9,3,1]}
dict2 = {red:[2,1,3], blue:[9,7,6], green:[3,9,1,3]}

In this case, the comparison must give a True as in both the dictionaries contains the same values in the list for a given key. I come up with a solution in which I access the list values using the key. Then check if the length of the lists are same and each value in one list present in the other list. I think there is some other way to do this in python. What is the best "pythonic" way to do this?

aaroh
  • 302
  • 2
  • 14
  • So basically comparing the lists without regard to order? – AKX Jul 27 '18 at 11:10
  • Yes, but it is lists inside a dictionary – aaroh Jul 27 '18 at 11:12
  • 1
    @aaroh then just add an extra layer of checks for the dictionary keys. – i alarmed alien Jul 27 '18 at 11:21
  • @aaroh You can try it: rslt= len(dict1)==len(dict2) and all( k1==k2 and sorted(l1)==sorted(l2) for (k1,l1),(k2,l2) in zip(sorted(dict2.items()),sorted(dict1.items())) ) – kantal Jul 27 '18 at 11:39
  • `list(map(sorted, dict1.values())) == list(map(sorted, dict2.values()))` ? – Chris_Rands Jul 27 '18 at 11:39
  • @kantal: there is no need to sort, and certainly not twice. – Martijn Pieters Jul 27 '18 at 11:43
  • 1
    @Chris_Rands: `Counter(a) == Counter(b)` lets you do the same test O(N) time. Don't use sorting unless the values are not hashable. – Martijn Pieters Jul 27 '18 at 11:44
  • 1
    Python 2 solution: `from collections import Counter`, then `a.viewkeys() == b.viewkeys() and all(Counter(v) == Counter(b[k]) for k, v in a.iteritems())`. This solves the issue in O(N) time, where `N` is the total number of entries in all lists, with an early exit whenever it is found that keys don't match or any of the value lists are not equal. – Martijn Pieters Jul 27 '18 at 11:46
  • @MartijnPieters like `list(map(Counter, dict1.values())) == list(map(Counter, dict2.values()))` ? – Chris_Rands Jul 27 '18 at 11:46
  • 1
    @Chris_Rands: This is Python 2, so `map()` doesn't need `list()`. And you are assuming that the keys are going to be listed in the same order, which they often will not. – Martijn Pieters Jul 27 '18 at 11:47
  • 1
    @Chris_Rands: you need to test if the keys are equal first too. `a.viewkeys() == b.viewkeys()` does a set comparison between the two key collections, efficiently. Then you need to ensure that you compare the counters *per paired key*. And you want to exit early if there is a mismatch, and avoid creating more `Counter()` objects when you already know the answer, hence the use of `all()`. – Martijn Pieters Jul 27 '18 at 11:49
  • @Martijn Pieters "there is no need to sort, and certainly not twice." sorted(dict1.items()) sorts really by the keys only for pairing in zip(), and sorted(l1) sorts by the value(i.e.sublist). – kantal Jul 27 '18 at 12:09
  • @kantal: yes, I know. You don't need to sort the keys, because you can loop over the keys of one dict to look them up in the other. And you don't need to sort the values, because counting them instead in O(N) time would suffice. – Martijn Pieters Jul 27 '18 at 12:13
  • @aaroh: that's not something I can help with, because the code I posted can't throw that error and I can't see what code you are using that throws that error. That error is not specific to using a `Counter()`. – Martijn Pieters Jul 27 '18 at 12:32
  • @aaroh: if you are having trouble implementing the suggestions made, you could post a new question with a [mcve] that reproduces the problem so we can help. – Martijn Pieters Jul 27 '18 at 12:33
  • @Martijn Pieters "Don't use sorting unless the values are not hashable." This is the point, you're right. – kantal Jul 27 '18 at 16:24

3 Answers3

0
dict1 = {"red": [1, 2, 3], "blue": [7, 6, 9], "green": [3, 9, 3, 1]}
dict2 = {"red": [2, 1, 3], "blue": [9, 7, 6], "green": [3, 9, 1, 3]}


def compare_dicts(a, b):
    if set(a.keys()) != set(b.keys()):
        # If the keys do not match,
        # the dicts can't be equal.
        return False

    for key, value in a.items():
        if key not in b:
            # If a key does not exist in `b`,
            # but exists in `a`, the dicts
            # can't be equal.
            return False

        if sorted(value) != sorted(b[key]):
            # If the sorted lists of values aren't
            # equal, the dicts can't be equal.
            return False

            # Every other case failed to exit, so the dicts
            # must be equal.
    return True


print(compare_dicts(dict1, dict2))
AKX
  • 152,115
  • 15
  • 115
  • 172
  • Sorting means I am increasing the complexity, is there any better way? – aaroh Jul 27 '18 at 11:38
  • Don't use sorting, use `collection.Counter` objects: `Counter(value) != Counter(b[key])`. And don't use `set()` objects, Python [dictionary view objects](https://docs.python.org/2/library/stdtypes.html#dictionary-view-objects) are sets and can be compared directly: `a.viewkeys() != b.viewkeys()`. – Martijn Pieters Jul 27 '18 at 11:40
  • Your `if key not in b:` test is redundant, you already determined that the keys in both dictionaries match. Do not use `a.items()` in Python 2, use `a.iteritems()` or `a.viewitems()`. – Martijn Pieters Jul 27 '18 at 11:40
  • `return a.viewkeys() == b.viewkeys() and all(Counter(v) == Counter(b[k]) for k, v in a.iteritems())` suffices. – Martijn Pieters Jul 27 '18 at 11:42
-1

If you wish compare two lists ignoring values order, you can convert each list to set

set(list1) == set(list2)
Pavel Minenkov
  • 368
  • 2
  • 9
-1

You can use set to compare two lists containing same elements but in different order

>>> dict1 = {'red':[1,2,3], 'blue':[7,6,9], 'green':[3,9,3,1]}
>>> dict2 = {'red':[2,1,3], 'blue':[9,7,6], 'green':[3,9,1,3]}
>>> all(set(v) == set(dict2.get(k)) for k,v in dict1.items())
True

If the dict values can be repeated, use sorted to compare

>>> all(sorted(v) == sorted(dict2.get(k)) for k,v in dict1.items())
True
Sunitha
  • 11,777
  • 2
  • 20
  • 23