A key can be a function that returns a tuple:
s = sorted(s, key = lambda x: (x[1], x[2]))
Or you can achieve the same using itemgetter (which is faster and avoids a Python function call):
import operator
s = sorted(s, key = operator.itemgetter(1, 2))
And notice that here you can use sort instead of using sorted and then reassigning:
s.sort(key = operator.itemgetter(1, 2))
Answer from Mark Byers on Stack OverflowA key can be a function that returns a tuple:
s = sorted(s, key = lambda x: (x[1], x[2]))
Or you can achieve the same using itemgetter (which is faster and avoids a Python function call):
import operator
s = sorted(s, key = operator.itemgetter(1, 2))
And notice that here you can use sort instead of using sorted and then reassigning:
s.sort(key = operator.itemgetter(1, 2))
I'm not sure if this is the most pythonic method ... I had a list of tuples that needed sorting 1st by descending integer values and 2nd alphabetically. This required reversing the integer sort but not the alphabetical sort. Here was my solution: (on the fly in an exam btw, I was not even aware you could 'nest' sorted functions)
a = [('Al', 2),('Bill', 1),('Carol', 2), ('Abel', 3), ('Zeke', 2), ('Chris', 1)]
b = sorted(sorted(a, key = lambda x : x[0]), key = lambda x : x[1], reverse = True)
print(b)
[('Abel', 3), ('Al', 2), ('Carol', 2), ('Zeke', 2), ('Bill', 1), ('Chris', 1)]
How to sort a dictionary where keys have multiple values?
Use a lambda as your sort function.
# lambda r: r[1][2]
# r == ('John Adams', ('111223333', 'A', 91.0))
# r[1] == ('111223333', 'A', 91.0)
# r[1][2] == 91.0
#
d = {
'John Adams': ('111223333', 'A', 91.0),
'Willy Smith Jr.': ('222114444', 'C', 77.55),
'Phil Jordan': ('777886666', 'F', 59.5)
}
for key, value in sorted(d.items(), key=lambda r: r[1][2]):
print(key, value)Produces:
('Phil Jordan', ('777886666', 'F', 59.5))
('Willy Smith Jr.', ('222114444', 'C', 77.55))
('John Adams', ('111223333', 'A', 91.0))
By the way, if you end up with multiple entries sharing the same sort value, you can build a compound sort (secondary, tertiary, etc). Your lambda should just return a tuple: lambda r: (r[1][2], r[0]), which would sort by that last value, and then by name if multiple values equal each other.
Here's without lambda:
def key_func(key_value_tuple):
name = key_value_tuple[0]
long_str_num, a_to_f, number = key_value_tuple[1]
return (number, name)
d = {
'John Adams': ('111223333', 'A', 91.0),
'Willy Smith Jr.': ('222114444', 'C', 77.55),
'Phil Jordan': ('777886666', 'F', 59.5)
}
for key, value in sorted(d.items(), key=key_func):
print(key, value)Edit: Added non-lambda option.
More on reddit.comsorting with key=lambda
Videos
So say this is my dictionary: {'John Adams': ('111223333', 'A', 91.0), 'Willy Smith Jr.': ('222114444', 'C', 77.55), 'Phil Jordan': ('777886666', 'F', 59.5)} and i want to sort it by the third value of each key (eg the 91.0 for John Adams). How would I go about doing that?
Use a lambda as your sort function.
# lambda r: r[1][2]
# r == ('John Adams', ('111223333', 'A', 91.0))
# r[1] == ('111223333', 'A', 91.0)
# r[1][2] == 91.0
#
d = {
'John Adams': ('111223333', 'A', 91.0),
'Willy Smith Jr.': ('222114444', 'C', 77.55),
'Phil Jordan': ('777886666', 'F', 59.5)
}
for key, value in sorted(d.items(), key=lambda r: r[1][2]):
print(key, value)
Produces:
('Phil Jordan', ('777886666', 'F', 59.5))
('Willy Smith Jr.', ('222114444', 'C', 77.55))
('John Adams', ('111223333', 'A', 91.0))
By the way, if you end up with multiple entries sharing the same sort value, you can build a compound sort (secondary, tertiary, etc). Your lambda should just return a tuple: lambda r: (r[1][2], r[0]), which would sort by that last value, and then by name if multiple values equal each other.
Here's without lambda:
def key_func(key_value_tuple):
name = key_value_tuple[0]
long_str_num, a_to_f, number = key_value_tuple[1]
return (number, name)
d = {
'John Adams': ('111223333', 'A', 91.0),
'Willy Smith Jr.': ('222114444', 'C', 77.55),
'Phil Jordan': ('777886666', 'F', 59.5)
}
for key, value in sorted(d.items(), key=key_func):
print(key, value)
Edit: Added non-lambda option.
Here's an extension of u/totallygeek's non-lambda solution, using a more advanced Python feature:
d = {'John Adams': ('111223333', 'A', 91.0), 'Willy Smith Jr.': ('222114444', 'C', 77.55),
'Phil Jordan': ('777886666', 'F', 59.5)}
def sortindex(index):
def key_func(item):
key, value = item
return (value[index], key)
return key_func
for key, value in sorted(d.items(), key=sortindex(2)):
print(key, value)
Here, key_func is wrapped inside another function, sortindex, which returns key_func. In other words, the code above does the same thing as this
def key_func(item):
key, value = item
return (value[2], key)
for key, value in sorted(d.items(), key=key_func):
print(key, value)
But notice that the sortindex function allows us to keep the tuple index as a free variable, so that we can also sort on sortindex(0) or sortindex(1).
This technique of wrapping one function inside another is called a closure.
If your first element in the tuple is an integer, you can sort by its negative value:
sorted(theList, key=lambda (num, letter): (-num, letter))
According to Python documentation:
The built-in sorted() function is guaranteed to be stable. A sort is stable if it guarantees not to change the relative order of elements that compare equal — this is helpful for sorting in multiple passes (for example, sort by department, then by salary grade).
Therefore, an easy way to do it will be doing a two-pass sorting:
sorted(theList, key = lambda x:x[1])
sorted(theList, key = lambda x:x[0], reverse = True)
Or if you really want a one-liner, you can do this:
sorted(thisList, key = lambda x:x[0] * 26 - (ord(x[1]) - ord('a')), reverse = True)
This one-liner assumes that all the characters are lowercase. What it does is that it makes each two consecutive numbers have a step of 26 instead of 1, and the characters a-z are mapped to 0-25, as a finer step. Since the characters are ascending order, we subtract it from the scaled number value.
This one-liner is kind of a hack, since it wouldn't work if the x[1] has no range (i.e. also a number)