Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unordered list as dict key [closed]

I want to be able to do something like:

foo = Counter(['bar', 'shoo', 'bar'])
tmp = {}
tmp[foo] = 5

In other words, is there a hashable equivalent for Counter? Note that I can't use frozenset since I have repeated elements that I want to keep in the key.

Edit: In my actual application, the objects in foo may not be comparable with each other so the list cannot be sorted.

like image 248
secluded Avatar asked Oct 30 '20 22:10

secluded


People also ask

Can you use a list as a key for a dictionary?

A dictionary or a list cannot be a key. Values, on the other hand, can literally be anything and they can be used more than once.

Can we pass list as key in dictionary Python?

We can use integer, string, tuples as dictionary keys but cannot use list as a key of it .

Why lists Cannot be used as dictionary keys?

You cannot use a list as a key because a list is mutable. Similarly, you cannot use a tuple as a key if any of its elements are lists. (You can only use a tuple as a key if all of its elements are immutable.)

Does dict keys return a list?

The dict. keys() method in Python Dictionary, returns a view object that displays a list of all the keys in the dictionary in order of insertion.


3 Answers

What you seem to require is a way to use unordered pairs of key-amount as keys. A frozenset is probably the way to go, although you will have to create it out of the items of the Counter and not its keys.

foo = Counter(['bar', 'shoo', 'bar'])
tmp = {}
tmp[frozenset(foo.items())] = 5

# tmp: {frozenset({('bar', 2), ('shoo', 1)}): 5}

If this is satisfying, you could implement this transformation by defining you own mapping type like so:

from collections import Counter

class CounterDict:
    def __init__(self):
        self.data = {}

    def __setitem__(self, key, value):
        if isinstance(key, Counter):
            self.data[frozenset(key.items())] = value
        else:
            raise TypeError

    def __getitem__(self, key):
        if isinstance(key, Counter):
            return self.data[frozenset(key.items())]
        else:
            raise TypeError

foo = Counter(['bar', 'shoo', 'bar'])
tmp = CounterDict()
tmp[foo] = 42
tmp[foo] # 42

You could make this implementation richer by making CounterDict a subclass of collections.UserDict.

like image 181
Olivier Melançon Avatar answered Oct 12 '22 19:10

Olivier Melançon


There are a couple of things you could do. One is to sort the list and convert the result to a tuple. That will work fine for small lists.

If you have large lists (with duplicates), you could convert it into a frozenset whose elements are tuples which are (word, count) pairs. So for the example, if your list is ['bar', 'shoo', 'bar'], you would have frozenset({('bar', 2), ('shoo', 1)}).

You could construct this by using Counter, or else just construct a dict of word: count entries, then convert the entries to tuples and construct a frozenset from them.

Both of these would be hashable, and support duplicate elements in the original list.

like image 37
Tom Karzes Avatar answered Oct 12 '22 20:10

Tom Karzes


Using the string representation of the Counter?

foo = Counter(['bar', 'shoo', 'bar'])
tmp = {}

tmp[str(foo)] = 5
like image 21
Nikaido Avatar answered Oct 12 '22 21:10

Nikaido