27

Say I have

votes = {'Charlie': 20, 'Able': 10, 'Baker': 20, 'Dog': 15}

I understand

print(sorted(votes.items(), key=lambda x: x[1]))

will lead to

[('Able', 10), ('Dog', 15), ('Baker', 20), ('Charlie', 20)]

But how does this work?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Cheese
  • 401
  • 1
  • 4
  • 7
  • I see, thanks, so what if I changed the key to return a tuple though? What's happening there? `key = lambda x: (-x[1],x[0])` – Cheese Aug 26 '15 at 23:30
  • 1
    That would sort by the tuples `(-value, key)`. It would end up sorting by the values from greatest to least, and breaking ties with the keys. The result would be `[('Baker', 20), ('Charlie', 20), ('Dog', 15), ('Able', 10)]`. – Cyphase Aug 26 '15 at 23:31
  • Oh ok, so what's the purpose of having the `x[0]` key aspect in the tuple if it's ignored by the sort? I guess I mean, why would I want to return a tuple there in the lambda? – Cheese Aug 26 '15 at 23:38
  • The `x[0]` is not ignored; it's used to break ties with the first item in the tuple. That's why `('Baker', 20)` will always come before `('Charlie', 20)` – Cyphase Aug 26 '15 at 23:40
  • So since they have the same value, the `x[0]` allows those with equivalent values to be sorted by their key? And I'm sorry I don't quite know what you mean by "break ties with the keys" – Cheese Aug 26 '15 at 23:45
  • 2
    When sorting tuples, Python first sorts by the first value. Then, if there are any ties (i.e. two or more tuples have the same first value), it sorts by the second value, then the third if there are any ties in the second value, etc. Think of it like sorting words in alphabetical order. First you sort by the first letter; if there are any ties, you sort by the second letter; you keep going until there are no more ties, or you run out of letters. – Cyphase Aug 26 '15 at 23:48

4 Answers4

12

The function you pass in to key is given each of the items that are being sorted, and returns a "key" that Python can sort by. So, if you want to sort a list of strings by the reverse of the string, you could do this:

list_of_strings.sort(key=lambda s: s[::-1])

This lets you specify the value each item is sorted by, without having to change the item. That way, you don't have to build a list of reversed strings, sort that, then reverse them back.

# DON'T do this

data = ['abc', 'def', 'ghi', 'jkl']
reversed_data = [s[::-1] for s in data]
reversed_data.sort()
data = [s[::-1] for s in reversed_data]

# Do this

data.sort(key=lambda s: s[::-1])

In your case, the code is sorting each item by the second item in the tuple, whereas normally it would initially sort by the first item in the tuple, then break ties with the second item.

Cyphase
  • 11,502
  • 2
  • 31
  • 32
7
>>> votes = {'Charlie': 20, 'Able': 10, 'Baker': 20, 'Dog': 15}

If we apply .items() on the votes dictionary above we get:

>>> votes_items=votes.items()
>>> votes_items
[('Charlie', 20), ('Baker', 20), ('Able', 10), ('Dog', 15)]
#a list of tuples, each tuple having two items indexed 0 and 1

For each tuple, the first index [0] are the strings ('Charlie','Able','Baker','Dog') and the second index [1] the integers (20,10,20,15).

print(sorted(votes.items(), key = lambda x: x[1])) instructs python to sort the items(tuples) in votes using the second index [1] of each tuple, the integers, as the basis of the sorting.

Python compares each integer from each tuple and returns a list that has ranked each tuple in ascending order (this can be reversed with the reverse=True argument) using each tuple's integer as the key to determine the tuple's rank,

Where there is a tie in the key, the items are ranked in the order they are originally in the dictionary. (so ('Charlie', 20) is before ('Baker', 20) because there is a 20==20 tie on the key but ('Charlie', 20) comes before ('Baker', 20) in the original votes dictionary).

The output then is:

 [('Able', 10), ('Dog', 15), ('Charlie', 20), ('Baker', 20)]

I hope this makes it easier to understand.

Samuel Kazeem
  • 787
  • 1
  • 8
  • 15
3

key is a function that will be called to transform the collection's items before they are compared. The parameter passed to key must be something that is callable.

The use of lambda creates an anonymous function (which is callable). In the case of sorted the callable only takes one parameters. Python's lambda is pretty simple. It can only do and return one thing really.

CodeIsLife
  • 1,205
  • 8
  • 14
3

The key parameter takes a function as its value, which is applied to each element before sorting, so that the elements are sorted based on the output of this function.

For example if you want to sort a list of strings based on their length, you can do something like this:

    list = ['aaaaaa', 'bb', 'ccc', 'd']
    sorted(list, key=len)

    # ['d', 'bb', 'ccc', 'aaaaaa']
Mustapha-Belkacim
  • 1,653
  • 1
  • 15
  • 19