Search code examples
pythoncounter

Does Counter from collections maintain order after frequency has been considered, if all other frequencies are the same?


Does Counter from collections maintain order after frequency has been considered?

e.g.

s= "leetcode"
cnt = Counter(s)
for el in s:
    if cnt[el] == 1:
        return el

So for the above code, the output looks something like:

Counter({'e': 3, 'l': 1, 't': 1, 'c': 1, 'o': 1, 'd': 1})

Now, I know Counter presents key/value in descending order, hence 'e' appears first as it has the highest count of 3, but after this all other elements have a counter of 1- does this mean that the Counter will always maintain the order of the string/array? It clearly does for the above example, but I wanted to make sure it isn't just my IDE that does this (Pycharm)


Solution

  • A Counter is really just a dict with some extra functionality. The underlying dictionary retains insertion order in recent versions of Python, as is called out in the docs:

    Changed in version 3.7: As a dict subclass, Counter Inherited the capability to remember insertion order.

    You can see this if you e.g. print the keys, which keep the order they were seen in the source string:

    >>> print(cnt.keys())
    dict_keys(['l', 'e', 't', 'c', 'o', 'd'])
    

    However, note that the __repr__ has specific handling for order:

    def __repr__(self):
        if not self:
            return f'{self.__class__.__name__}()'
        try:
            # dict() preserves the ordering returned by most_common()
            d = dict(self.most_common())
        except TypeError:
            # handle case where values are not orderable
            d = dict(self)
        return f'{self.__class__.__name__}({d!r})'
    

    The Counter is displayed in most_common order, whose current documentation says:

    Elements with equal counts are ordered in the order first encountered

    The implementation uses sorted, which is:

    ...guaranteed to be stable. A sort is stable if it guarantees not to change the relative order of elements that compare equal...

    hence retaining the insertion order of elements with equal counts.

    Earlier versions of the most_common docs, though, say:

    Elements with equal counts are ordered arbitrarily

    because the dictionary was arbitrarily ordered to begin with.