0

I have a counter that I would like to sort in the following way:

Counter({'M': 9, 'L': 5, 'P': 5, 'S': 2, 'd': 1, 'T': 1})

when I use my code, here is what it gives me:

Counter({'M': 9, 'P': 5, 'L': 5, 'S': 2, 'T': 1, 'd': 1})

I tried the function sorted(), but when I use it, its return value is not a counter anymore.

Here is my code, how would you do that?

def most_encountered_letters(dictionnary):
    counter = collections.Counter()
    for line in dictionnary:
        words = line.split(',')[0].split(' ')
        for word in words:
            counter[word[0]] += 1

    print(counter)                                                                                                        
    return counter.most_common(5)
alex
  • 11
  • 1
  • 1
  • 1
    A `Counter` is a `Dict` subclass, so by definition it is unsorted, and if you try to use `sorted()` it has to return a non-`Dict` class. You could move the result into an [`OrderedDict`](https://stackoverflow.com/a/9001529/1270789). I suppose, but my question is "Why do you want to sort?", as your actual issue might be better addressed another way. Finally, here's [lots of ways to sort](https://wiki.python.org/moin/HowTo/Sorting). BTW, Python 2.x or 3.x? – Ken Y-N Jul 12 '17 at 02:08
  • It is no way for sorting a `Dict` in python, the only way is look into the link https://stackoverflow.com/questions/613183/sort-a-python-dictionary-by-value?page=1&tab=votes#tab-top – BENY Jul 12 '17 at 02:11
  • _Be precise_! Your not-an-answer drastically changed your question, and invalidated both answers. – Kevin J. Chase Jul 12 '17 at 03:05
  • Also, [edit] your title. Your title says you want to sort by key first and value second, but it sounds like you actually want to sort by value first and key second, which is the opposite. – Kevin J. Chase Jul 12 '17 at 03:23

1 Answers1

3

Counters are unordered. They are a subclass of dict, and like dict, are not ordered. It doesn't make sense to say "a sorted Counter". You can get a list of the items in Counter sorted the way you want, e.g.:

>>> from collections import Counter
>>> c = Counter({'M': 9, 'L': 5, 'P': 5, 'S': 2, 'd': 1, 'T': 1})
>>> c
Counter({'M': 9, 'L': 5, 'P': 5, 'S': 2, 'T': 1, 'd': 1})
>>> sorted(c.items(), key= lambda t: (t[1], t[0]), reverse=True)
[('M', 9), ('P', 5), ('L', 5), ('S', 2), ('d', 1), ('T', 1)]

If you want an ordered Mapping type, you either have to use the built-in OrderedDict, or implement your own. We can use multiple inheritance to re-use built-in classes to get what we want. This is a recipe straight from the docs:

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),)

So, in action:

>>> oc = OrderedCounter()
>>> for k,v in sorted(c.items(), key= lambda t: (t[1], t[0]), reverse=True):
...     oc[k] = v
...
>>> oc
OrderedCounter(OrderedDict([('M', 9), ('P', 5), ('L', 5), ('S', 2), ('d', 1), ('T', 1)]))
>>> for k,v in oc.items():
...   print(k,v)
...
M 9
P 5
L 5
S 2
d 1
T 1
>>>

More importantly, you should consider why you need an ordered Counter... do you really need one?

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172