Sort a list of tuples by value and then alphabetically

10,178

Solution 1

You can use the sorted function:

sorted_by_medals = sorted(list_of_medals, key=lambda tup: (-tup[1], tup[0]))

Solution 2

In this instance, I'd use a lambda function as the key argument to sort()/sorted():

In [59]: sorted(list_of_medals, key=lambda x:(-x[1],x[0]))
Out[59]: 
[('Sweden', 24),
 ('Germany', 16),
 ('Ireland', 10),
 ('Russia', 10),
 ('Spain', 9),
 ('Albania', 8),
 ('Lithuania', 7),
 ('Iceland', 6),
 ('Italy', 5),
 ('Malta', 5),
 ('Estonia', 4),
 ('Serbia', 4),
 ('Turkey', 4),
 ('Azerbaijan', 2),
 ('Moldova', 2)]

The negation of x[1] is needed to sort the medals in descending order while sorting country names in ascending order (simply setting reverse=True wouldn't achieve that).

As several people have pointed out in the comments, a more general way to do a complex sort on a compound key is to perform several sorting steps. To do this, sort on one component at a time, starting with the least significant one:

In [67]: temp = sorted(list_of_medals, key=itemgetter(0))

In [68]: sorted(temp, key=itemgetter(1), reverse=True)
Out[68]: 
[('Sweden', 24),
 ('Germany', 16),
 ('Ireland', 10),
 ('Russia', 10),
 ...

This relies on the fact that Python's sort is stable, meaning that items that compare equal are never reordered.

Share:
10,178
user1830011
Author by

user1830011

Updated on June 09, 2022

Comments

  • user1830011
    user1830011 almost 2 years

    Bit of a python newbie, but I got the following list of tuples. I need to sort it by value and if the value is the same, solve ties alphabetically. Here's a sample:

    #original
    list_of_medals = [('Sweden', 24), ('Germany', 16), ('Russia', 10), ('Ireland', 10), ('Spain', 9), ('Albania', 8), ('Lithuania', 7), ('Iceland', 6), ('Malta', 5), ('Italy', 5), ('Serbia', 4), ('Estonia', 4), ('Turkey', 4), ('Moldova', 2), ('Azerbaijan', 2)]
    #                                                              \____/                                                                                                                      \_____/                                         \______/
    #after sorting                                                 /    \                                                                                                                      /     \                                         /      \
    sorted_medals  = [('Sweden', 24), ('Germany', 16), ('Ireland', 10), ('Russia', 10), ('Spain', 9), ('Albania', 8), ('Lithuania', 7), ('Iceland', 6), ('Malta', 5), ('Italy', 5), ('Estonia', 4), ('Serbia', 4), ('Turkey', 4), ('Azerbaijan', 2), ('Moldova', 2)]
    

    Is it perhaps possible with the operator module?

  • NPE
    NPE over 11 years
    This ignores the country name and thus doesn't meet the spec.
  • bmu
    bmu over 11 years
    this doesn't sort alphabetically if the values are the same
  • Steven Rumbalski
    Steven Rumbalski over 11 years
    Missed requirement: "if the value is the same, sorted alphabetically."
  • mgilson
    mgilson over 11 years
    I think that it is also worth mentioning the fact that python's sort is "stable". While you don't need it in this case because you can easily sort big->small just by -x[1], with other types it isn't quite so easy and you may be forced to do the sorting in 2 steps.
  • Katriel
    Katriel over 11 years
    You could also sort twice, because Python sort is stable.
  • mgilson
    mgilson over 11 years
    Won't this now sort reversed alphabetically if the numbers are the same?
  • jfs
    jfs over 11 years
    @mgilson: could you provide an example where you can't use key function and have to use 2-step sort?
  • NPE
    NPE over 11 years
    @J.F.Sebastian: Let's say instead of the numeric medal count we had a string that had to be sorted lexicographically in descending order. The negation trick wouldn't work, and the two sorts would probably be the cleanest way to do the compound ascending/descending sort.
  • mgilson
    mgilson over 11 years
    @NPE -- Thanks, that was the exact example I was going to give.
  • jfs
    jfs over 11 years
    @mgilson: notice that I've used "can't" and "have to". "forced to" is not the synonym for "the cleanest". Custom cmp function with cmp_to_key can handle it e.g., lambda x,y: cmp(y[1], x[1]) or cmp(x[0], y[0]).
  • mgilson
    mgilson over 11 years
    @J.F.Sebastian -- Ok, that's fair. Given a situation, you probably could come up with something that would work, but as you say, it's definitely not the cleanest.