41

I need to sort a dictionary by first, values with reverse=True, and for repeating values, sort by keys, reverse=False

So far, I have this

dict = [('B', 3), ('A', 2), ('A', 1), ('I', 1), ('J', 1)]
sorted(dict.items(), key=lambda x: (x[1],x[1]), reverse=True)

which returns...

[('B', 3), ('A', 2), ('J', 1), ('I', 1), ('A', 1)]

but I need it to be:

[('B', 3), ('A', 2), ('A', 1), ('I', 1), ('J', 1)]

as you can see, when values are equal, I can only sort the key in a decreasing fashion as specified... But how can I get them to sort in an increasing fashion?

mechanical_meat
  • 163,903
  • 24
  • 228
  • 223
Nicolas Hung
  • 595
  • 1
  • 6
  • 15
  • 4
    `dict` is a list -- And not even really a list suitable for turning into a dictionary as it would have repeated keys... – mgilson Jan 22 '13 at 19:07
  • got it to work thanks to mgilson, I was trying something similar but was sleepy and couldn't think straight. Thanks! – Nicolas Hung Jan 22 '13 at 20:44

3 Answers3

68

The following works with your input:

d = [('B', 3), ('A', 2), ('A', 1), ('I', 1), ('J', 1)]
sorted(d,key=lambda x:(-x[1],x[0]))

Since your "values" are numeric, you can easily reverse the sort order by changing the sign.

In other words, this sort puts things in order by value (-x[1]) (the negative sign puts big numbers first) and then for numbers which are the same, it orders according to key (x[0]).

If your values can't so easily be "negated" to put big items first, an easy work-around is to sort twice:

from operator import itemgetter
d.sort(key=itemgetter(0))
d.sort(key=itemgetter(1),reverse=True)

which works because python's sorting is stable.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 2
    As to the example above, if you use the itemgetter method, the priority is in reversed order: `d.sort(...)` <--3rd prio `d.sort(...)` <--2nd prio `d.sort(...)` <--1st prio – questionto42 May 21 '20 at 16:39
  • the itemgetter part is wrong, @Lorenz point is valid – kederrac Jul 15 '20 at 12:15
5
In [4]: l = [('B', 3), ('A', 2), ('A', 1), ('I', 1), ('J', 1)]

In [5]: sorted(l, key=lambda (x,y):(-y,x))
Out[5]: [('B', 3), ('A', 2), ('A', 1), ('I', 1), ('J', 1)]
NPE
  • 486,780
  • 108
  • 951
  • 1,012
2

you can use collections.defaultdict:

In [48]: from collections import defaultdict

In [49]: dic=[('B', 3), ('A', 2), ('A', 1), ('I', 1), ('J', 1)]

In [50]: d=defaultdict(list)

In [51]: for x,y in dic:
    d[y].append(x)
    d[y].sort()          #sort the list

now d is something like:

 defaultdict(<type 'list'>, {1: ['A', 'I', 'J'], 2: ['A'], 3: ['B']}

i.e. A new dict with 1,2,3... as keys and corresponding alphabets stored in lists as values.

Now you can iterate over the sorted(d.items) and get the desired result using itertools.chain() and itertools.product().

In [65]: l=[ product(y,[x]) for x,y in sorted(d.items(),reverse=True)]

In [66]: list(chain(*l))
Out[66]: [('B', 3), ('A', 2), ('A', 1), ('I', 1), ('J', 1)]
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • I'm not really sure what you're showing here ... You probably should offer a little more explanation :P – mgilson Jan 22 '13 at 19:15