Klaas
Klaas

Reputation: 55

Python: sort a dictionary by values while maintain alphabetically in case of ties

So, I want to sort a dictionary by values and maintain alphabetically in case of ties if the item values are of the same. However, the python dictionary does not save data alphabetically, how to fix it? Thank you in advance!

Here is my code:

from collections import Counter
test = "betty bought a bit of butter but the butter was bitter"
Counter(test.split()).most_common(3)

Output:

[('butter', 2), ('a', 1), ('bitter', 1)]

The desired output should be:

[('butter', 2), ('a', 1), ('betty', 1)]

since that 'bitter' should be behind 'betty' alphabetically.

Upvotes: 1

Views: 956

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1123450

Use the heapq.nsmallest() function with a custom key, passing in a negative count to invert the sort for that part, and the key itself to be returned in alphabetical order:

import heapq

top3 = heapq.nsmallest(
    3, Counter(test.split()).items(),
    key=lambda kv: (-kv[1], kv[0]))

The Counter.most_common() method uses heapq.nlargest() when you give it an argument that is smaller than the number of keys in the dictionary and only uses the counts to determine order, so in case of ties the order is arbitrary. The above is the equivalent for your specific sort order. Just like Counter.most_common(), this is a O(NlogK) solution (N being the number of items counted, K the number of items you want output).

Demo:

>>> import heapq
>>> from collections import Counter
>>> test = "betty bought a bit of butter but the butter was bitter"
>>> heapq.nsmallest(3, Counter(test.split()).items(), key=lambda kv: (-kv[1], kv[0]))
[('butter', 2), ('a', 1), ('betty', 1)]

Upvotes: 5

Jon Clements
Jon Clements

Reputation: 142206

Alternatively - you could mix Counter and OrderedDict:

from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
    pass

test = "betty bought a bit of butter but the butter was bitter"
mc = OrderedCounter(sorted(test.split())).most_common(3)
# [('butter', 2), ('a', 1), ('betty', 1)]

Upvotes: 2

Related Questions