Have you ever heard or used python counters? They are very useful to count the number of occurrences of “simple” items. Basically:
> from collections import Counter > colors = ['red', 'blue', 'red', 'green'] > Counter(colors) Counter({'red': 2, 'blue': 1, 'green': 1})
However, if you try to use it on non hashable types it doesn’t work.
> colors = [['red', 'warm'], ['blue', 'cold'], ['red', 'warm']] > Counter(colors) [...] TypeError: unhashable type: 'list'
What do we do then?
Once you know the trick, it’s quite simple. You build an object that will hold your data and you define __hash__
and __eq__
.
class StringList(object): def __init__(self, val): self.val = val def __hash__(self): return hash(str(self.val)) def __repr__(self): # Bonus: define this method to get clean output return str(self.val) def __eq__(self, other): return str(self.val) == str(other.val)
It’s a bit more overhead, but it makes it work.
> colors = [StringList(['red', 'warm']), . StringList(['blue', 'cold']), . StringList(['red', 'warm'])] > print(Counter(colors)) Counter({['red', 'warm']: 2, ['blue', 'cold']: 1})
You can also easily iterate over the items.
> for k, v in Counter(colors).items(): . print(k, v) ['blue', 'cold'] 1 ['red', 'warm'] 2
As a side note, in this example the order of the items within StringList objects is important.
Thanks to Simon Lemieux for helping me out with that.
EDIT
That particular example could have work by simply using tuples instead.
> colors = [('red', 'warm'), ('blue', 'cold'), ('red', 'warm')] > Counter(colors) Counter({('red', 'warm'): 2, ('blue', 'cold'): 1})