[ACCEPTED]-How to get count dict of items but maintain the order in which they appear?-ordereddictionary

Accepted answer
Score: 12

You can use the recipe that uses collections.Counter and collections.OrderedDict:

from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
    'Counter that remembers the order elements are first encountered'

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

    def __reduce__(self):
        return self.__class__, (OrderedDict(self),)

words = ["oranges", "apples", "apples", "bananas", "kiwis", "kiwis", "apples"]
c = OrderedCounter(words)
# OrderedCounter(OrderedDict([('oranges', 1), ('apples', 3), ('bananas', 1), ('kiwis', 2)]))


Score: 12

On Python 3.6+, dict will now maintain insertion 14 order.

So you can do:

words = ["oranges", "apples", "apples", "bananas", "kiwis", "kiwis", "apples"]
for w in words: counter[w]=counter.get(w, 0)+1
>>> counter
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

Unfortunately, the Counter 13 in Python 3.6 and 3.7 does not display the 12 insertion order that it maintains; instead, __repr__ sorts the return by 11 the most to least common.

But you can use 10 the same OrderedDict recipe but just use the Python 9 3.6+ dict instead:

from collections import Counter

class OrderedCounter(Counter, dict):
    'Counter that remembers the order elements are first encountered'
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, dict(self))

    def __reduce__(self):
        return self.__class__, (dict(self),)

>>> OrderedCounter(words)
OrderedCounter({'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2})

Or, since Counter is a 8 subclass of dict that maintains order in Python 7 3.6+, you can just avoid using Counter's 6 __repr__ by either calling .items() on the counter or turning 5 the counter back into a dict:

>>> c=Counter(words)

This presentation 4 of that Counter is sorted by most common 3 element to least and uses Counters __repr__ method:

>>> c
Counter({'apples': 3, 'kiwis': 2, 'oranges': 1, 'bananas': 1})

This 2 presentation is as encountered, or insertion 1 order:

>>> c.items()
dict_items([('oranges', 1), ('apples', 3), ('bananas', 1), ('kiwis', 2)])


>>> dict(c)
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}
Score: 1

In Python 3.6, dictionaries are insertion ordered, but 19 this is an implementation detail.

In Python 3.7+, insertion 18 order is guaranteed and can be relied upon. See 17 Are dictionaries ordered in Python 3.6+? for more details.

So, depending on your 16 Python version, you may wish to just use 15 Counter as is, without creating an OrderedCounter class as described 14 in the documentation. This works because Counter is a subclass 13 of dict, i.e. issubclass(Counter, dict) returns True, and therefore inherits 12 the insertion ordering behaviour of dict.

String representation

It 11 is worth noting the the string representation 10 for Counter, as defined in the repr method, has not been updated to reflect 9 the change in 3.6 / 3.7, i.e. print(Counter(some_iterable)) still returns 8 items from largest counts descending. You 7 can trivially return the insertion order 6 via list(Counter(some_iterable)).

Here are some examples demonstrating 5 the behaviour:

x = 'xyyxy'
print(Counter(x))         # Counter({'y': 3, 'x': 2}), i.e. most common first
print(list(Counter(x)))   # ['x', 'y'], i.e. insertion ordered
print(OrderedCounter(x))  # OC(OD([('x', 2), ('y', 3)])), i.e. insertion ordered


You should not use a regular 4 Counter if additional or overwritten methods available 3 to OrderedCounter are important to you. Of particular 2 note:

  1. OrderedDict and consequently OrderedCounter offer popitem and move_to_end methods.
  2. Equality tests between OrderedCounter objects are order-sensitive and are implemented as list(oc1.items()) == list(oc2.items()).

For example, equality tests will yield 1 different results:

Counter('xy') == Counter('yx')                # True
OrderedCounter('xy') == OrderedCounter('yx')  # False
Score: 0

Explained in comments

text_list = ['oranges', 'apples', 'apples', 'bananas', 'kiwis', 'kiwis', 'apples']

# create empty dictionary
freq_dict = {}
# loop through text and count words
for word in text_list:
    # set the default value to 0
    freq_dict.setdefault(word, 0)
    # increment the value by 1
    freq_dict[word] += 1
print(freq_dict )
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

[Program finished]


More Related questions