For example, I need to count how many times a word appears in a list, not sorted by frequency but with the order in which the words appear, i.e. insertion order.
from collections import Counter
words = ['oranges', 'apples', 'apples', 'bananas', 'kiwis', 'kiwis', 'apples']
c = Counter(words)
print(c)
So instead of: {'apples': 3, 'kiwis': 2, 'bananas': 1, 'oranges': 1}
I'd rather get: {'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}
And I don't really need this Counter
method, any way that will produce correct result is OK for me.
On Python 3.6+,
dict
will now maintain insertion order.So you can do:
Unfortunately, the Counter in Python 3.6 and 3.7 does not display the insertion order that it maintains; instead,
__repr__
sorts the return by the most to least common.But you can use the same OrderedDict recipe but just use the Python 3.6+ dict instead:
Or, since Counter is a subclass of
dict
that maintains order in Python 3.6+, you can just avoid using Counter's__repr__
by either calling.items()
on the counter or turning the counter back into adict
:This presentation of that Counter is sorted by most common element to least and uses Counters
__repr__
method:This presentation is as encountered, or insertion order:
Or,
You can use the recipe that uses
collections.Counter
andcollections.OrderedDict
:In Python 3.6, dictionaries are insertion ordered, but this is an implementation detail.
In Python 3.7+, insertion order is guaranteed and can be relied upon. See Are dictionaries ordered in Python 3.6+? for more details.
So, depending on your Python version, you may wish to just use
Counter
as is, without creating anOrderedCounter
class as described in the documentation. This works becauseCounter
is a subclass ofdict
, i.e.issubclass(Counter, dict)
returnsTrue
, and therefore inherits the insertion ordering behaviour ofdict
.String representation
It is worth noting the the string representation for
Counter
, as defined in therepr
method, has not been updated to reflect the change in 3.6 / 3.7, i.e.print(Counter(some_iterable))
still returns items from largest counts descending. You can trivially return the insertion order vialist(Counter(some_iterable))
.Here are some examples demonstrating the behaviour:
Exceptions
You should not use a regular
Counter
if additional or overwritten methods available toOrderedCounter
are important to you. Of particular note:OrderedDict
and consequentlyOrderedCounter
offerpopitem
andmove_to_end
methods.OrderedCounter
objects are order-sensitive and are implemented aslist(oc1.items()) == list(oc2.items())
.For example, equality tests will yield different results: