2
clip = [('b', 12), ('e', 10),('d', 11),('a',12),('c', 10),('g',18)]

In this list the letter that has the highest number should be printed first i.e. in ascending order. Now if two letters have same numbers than it the letter that comes first in English alphabet should be printed first and the next one accordingly. How can I do that? This above list should be printed like this:

clip = [('g',18),('a',12),('b',12),('d',11),('c',10),('e',10)]

I tried most sorting methods I know like :

sorted(clip.items(), key = lambda x:(x[0],x[1]))
Mohammad Yusuf
  • 16,554
  • 10
  • 50
  • 78
Jay Jay
  • 29
  • 7

1 Answers1

2

Python's sorted() is stable, so sort by alphabet first, then by number.

>>> clip = [('b', 12), ('e', 10),('d', 11),('a',12),('c', 10),('g',18)]
>>> sorted(sorted(clip), key=lambda x:x[1], reverse=True)
[('g', 18), ('a', 12), ('b', 12), ('d', 11), ('c', 10), ('e', 10)]

According to juanpa.arrivillaga in the comments, this is more efficient:

>>> sorted(clip, key=lambda x:(-x[1], x[0]))
[('g', 18), ('a', 12), ('b', 12), ('d', 11), ('c', 10), ('e', 10)]

Some help with understanding -x[1]:

Tuples, (a,b), are sorted by comparing a values and then comparing b values if two a values are equal. Sorting is always in ascending order unless told otherwise. If you have a bunch of numbers, [1,2,3], and you wanted to sort them in reverse you could tell python that their real values are * -1: [-1,-2,-3]. Now, sorting them would yield smallest-to-largest [-3,-2,-1]. When we pass a key function that negates the numbers, we're passing those negative numbers, [-1,-2,-3] for comparison as stand-in values for [1,2,3]. Here's some commands that you can type into a python interpreter to learn it first hand:

$ python3
Python 3.5.2 (default, Nov 14 2016, 15:04:53)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [(3,20), (1,40), (4,10), (2,30)]

Understanding how to manipulate the list for key functions:

>>> [x[0] for x in a]
[3, 1, 4, 2]
>>> [x[1] for x in a]
[20, 40, 10, 30]
>>> [-x[0] for x in a]
[-3, -1, -4, -2]
>>> [(x[1],x[0]) for x in a]
[(20, 3), (40, 1), (10, 4), (30, 2)]

Using what we've learned above with sorting. Remember that the key value is just a temporary stand-in for the real value while sorting:

>>> sorted(a)
[(1, 40), (2, 30), (3, 20), (4, 10)]
>>> sorted(a, key=lambda x: -x[0])
[(4, 10), (3, 20), (2, 30), (1, 40)]
>>> sorted([(x[1],x[0]) for x in a])
[(10, 4), (20, 3), (30, 2), (40, 1)]
>>> sorted([(-x[1],x[0]) for x in a])
[(-40, 1), (-30, 2), (-20, 3), (-10, 4)]

A key function doesn't have to make sense. This one always returns 0 and therefore, the list remains in the original order.

>>> sorted(a, key=lambda x: 0)
[(3, 20), (1, 40), (4, 10), (2, 30)]

One last bit, the key=lambda x:(-x[1], x[0]) trick only works for numbers or things that can be negated. If we tried to sort your list again, but in reverse by sorting on descending letters and ascending numbers, the -1 trick no longer works:

>>> clip = [('b', 12), ('e', 10),('d', 11),('a',12),('c', 10),('g',18)]

First field, letters, in reverse, then numbers in order

>>> sorted(clip, key=lambda x: (-x[0], x[1]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
TypeError: bad operand type for unary -: 'str'

First sort by reversed letters...

>>> sorted(clip, reverse=True)
[('g', 18), ('e', 10), ('d', 11), ('c', 10), ('b', 12), ('a', 12)]

then by numbers.

>>> sorted(sorted(clip, reverse=True), key=lambda x: x[1])
[('e', 10), ('c', 10), ('d', 11), ('b', 12), ('a', 12), ('g', 18)]
Community
  • 1
  • 1
Harvey
  • 5,703
  • 1
  • 32
  • 41