I'm interested in comparing multiple lists, taking the difference and iterating through that.
Both are list of dicts that contain the following keys: 'ssid' - str, 'bssid' - str, 'channel' - int, 'flags' - list, 'found' - bool
I've tried:
list = list(set(networks_list).difference(missing_networks))
But I receive the error:
unhashable type 'dict'
My data structure looks like this:
list: [{'found': False, 'flags': ['WPA2-PSK-CCMP', 'WPS', 'ESS'], 'ssid': 'SOHO_BROADCAST', 'bssid': '30:46:9a:9d:11:1a', 'channel': 1}, {'found': False, 'flags': ['WPA-EAP-TKIP', 'WPA2-EAP-CCMP', 'ESS'], 'ssid': 'Cisco 2.4ghz', 'bssid': '40:f4:ec:7f:3c:5a', 'channel': 11}, {'found': False, 'flags': ['WPA-EAP-TKIP', 'WPA2-EAP-CCMP', 'ESS'], 'ssid': 'Cisco 5.0ghz', 'bssid': '40:f4:ec:7f:3c:54', 'channel': 149}]
Missing networks is initially empty.
Is there a simple way of doing this?
This approach works:
Instead of making them list of dicts make them a list of objects which implement
__eq__
and__hash__
and the code you provide should workA dict is a mutable item. This means it has no constant hash value over the course of its life, and cannot be put into a set.
If you convert all the dicts to strings with the same function, they become hashable and you can use them in a set...
I'm going to go by Eric's answer here.
First, the problem at hand. Why is a dict unhashable? Simply put, because it's a mutable container. If you change the content of the dict, the hash changes. The same will happen for any other mutable container like lists. So, you have to use something that's immutable.
The simplest solution in my mind would be to use a wrapper class. Essentially, a class that has a single property being the dict you originally wanted. You can spice it up with whatever magic functions you want for comparisons.
So, if I have your original list of networks
I can easily apply a wrapper class.
That way, the dictionary is stored and accessible through
or whatever else you could possibly want to name it. Also, because of the way the class is implemented, you can use it to wrap whatever you want, even if there are multiple things per Wrapper!
What this does, as you might be well aware, is make is so what's actually getting hashed is the object, according to a unique id assigned to it at runtime. A little refactoring for your difference functions to work with these wrappers, and you should be well on your way (unless you come up with a better solution =D )
As mentioned earlier, dicts are mutable and therefor can't be operated by set() -- that's because there's no guarantee that once placed inside a set, a dict won't change and become equal to another existing element of the set, thus violating the set quality.
If you are only checking the dicts for equality, you can convert them to tuples, then use tuples in set() operations, then convert tuples in the resulting set back into dicts.
Wrapping dicts into classes can be quite a bit more cumbersome, as you still have to solve to conversion from a dict to a immutable data type.
As you have lists inside your dicts, you have more work. The simplest is if you could just replace lists with tuples in the original dicts.
Assuming that's not feasible, your conversion process will have to be a function, as opposed to just calling tuple() and dict(), respectively. You'll need to conver lists to tuples first, then converts dicts with tuples instead of lists to tuples. For example:
To convert back, you have two options. Either look at key values that you know correspond to lists (if your keys are known and don't change) or look at tuples where the second element is a tuple itself (of you don't store any tuples in the original dicts). If neither option applies, you have to write more complex conversion algorithms.
Use a list comprehension: