Sorted function in Python dictionary

Question:

I have a dictionary and I need to print the contents of dictionary first sorted by "value" if there is any value tie then I need to use "key" as a tie breaker so that the keys that have the identical values will be printed in alphabetic order.

final = OrderedDict(sorted(mydict.items(), key=lambda x: (x[1], x[0]), reverse=True))

for key, value in final.items():
    print(f'{key} : {value}')

but when the print command runs, it will display the output like the identical values have not been sorted as expected:

Action : 3
Romance : 2
Horror : 2
History : 2
Comedy : 2
Adventure : 1

The way it’s supposed to print must be like:

Action : 3
Comedy : 2
History : 2
Horror : 2
Romance : 2
Adventure : 1

Any thoughts about how can I correct this issue?

Answers:

{k: v for k, v in sorted(mydict.items(), key=lambda item: (-item[1], item[0]), reverse=False)}
Answered By: BloomShell

reverse is applied after the sequence has been sorted according to key. You only want the comparison on x[1] to be reversed, not the final ordering. I would use functools.cmp_to_key to convert a comparison function to a key function.

from functools import cmp_to_key

# Redefined from Python 2 since Python 3 dropped it
def cmp(x, y):
    return -1 if x < y else 0 if x == y else 1

def genre_sort(x, y):
    # A bigger count is a smaller genre; otherwise, sort alphabetically
    # by name
    return cmp(y[1], x[1]) or cmp(x[0], y[0])
    
final = OrderedDict(sorted(mydict.items(), key=cmp_to_key(genre_sort)))
Answered By: chepner

You want to reverse the sort for x[1] only so don’t pass reverse=True here, instead use negative number trick:

final = OrderedDict(sorted(mydict.items(), key=lambda x: (-x[1], x[0])))
Answered By: zero

Why this answer when there are already three other ones?

In my opinion none of the other answers exposed the core of the problem described in the question, which is by the way already sufficiently answered here: https://stackoverflow.com/questions/20145842/python-sorting-by-multiple-criteria and here https://stackoverflow.com/questions/55866762/how-to-sort-a-list-of-strings-in-reverse-order-without-using-reverse-true-parame
making it a candidate for becoming closed as a duplicate.

The feature of Python allowing to have a solution to the problem of sorting columns in different sorting orders is the fact that

Python’s sort is stable allowing you to use consecutive sorts, using the rightmost criteria first, then the next, etc. ( Martijn Pieters ).

That Python’s sort algorithm is stable means that equal elements keep their relative order. So you can use a first sort on the second element (sorting in ascending order) and then sort again, on only the first element and in reverse order.

I have changed the dictionary listed in the question to having only string values what will not allow to use the ‘trick’ with negative number values in the key function to achieve the desired result to demonstrate what is said above.

The code below:

mydict = { 
    'Romance'   : '2', 
    'Adventure' : '1', 
    'Action'    : '3', 
    'Horror'    : '2', 
    'History'   : '2',
    'Comedy'    : '2',
}
mylist = list(mydict.items())
print(mylist)
print()
mylist = sorted(mylist)
print(mylist)
mslist = sorted(mylist, key=lambda x: (x[1]), reverse=True)
print(mslist)
from collections import OrderedDict
final = OrderedDict(mslist)
for key, value in final.items():
    print(f'  {key:10} : {value}')

creating following output:

[('Romance', '2'), ('Adventure', '1'), ('Action', '3'), ('Horror', '2'), ('History', '2'), ('Comedy', '2')]

[('Action', '3'), ('Adventure', '1'), ('Comedy', '2'), ('History', '2'), ('Horror', '2'), ('Romance', '2')]
[('Action', '3'), ('Comedy', '2'), ('History', '2'), ('Horror', '2'), ('Romance', '2'), ('Adventure', '1')]
  Action     : 3
  Comedy     : 2
  History    : 2
  Horror     : 2
  Romance    : 2
  Adventure  : 1

demonstrates what I am speaking about here.

From:

[('Action', '3'), ('Adventure', '1'), ('Comedy', '2'), ('History', '2'), ('Horror', '2'), ('Romance', '2')]
[('Action', '3'), ('Comedy', '2'), ('History', '2'), ('Horror', '2'), ('Romance', '2'), ('Adventure', '1')]

can be seen that the second sort moves only '('Adventure', '1')' keeping the order of all the other items.

Answered By: Claudio
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.