In Python you sort with a tuple

My colleague Axel Hecht showed me something I didn’t know about sorting in Python.

In Python you can sort with a tuple. It’s best illustrated with a simple example:

>>> items = [(1, 'B'), (1, 'A'), (2, 'A'), (0, 'B'), (0, 'a')]>>> sorted(items)[(0, 'B'), (0, 'a'), (1, 'A'), (1, 'B'), (2, 'A')]

By default the sort and the sorted built-in function notices that the items are tuples so it sorts on the first element first and on the second element second.

However, notice how you get (0, 'B') appearing before (0, 'a'). That’s because upper case comes before lower case characters. However, suppose you wanted to apply some “humanization” on that and sort case insensitively. You might try:

>>> sorted(items, key=str.lower)Traceback (most recent call last):  File "", line 1, in TypeError: descriptor 'lower' requires a 'str' object but received a 'tuple'

which is an error we deserve because this won’t work for the first part of each tuple.

We could try to write a lambda function (e.g. sorted(items, key=lambda x: x.lower() if isinstance(x, str) else x)) but that’s not going to work because you’ll only ever get to apply that to the first item.

Without further ado, here’s how you do it. A lambda function that returns a tuple:

>>> sorted(items, key=lambda x: (x[0], x[1].lower()))[(0, 'a'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A')]

And there you have it! Thanks for sharing Axel!

As a bonus item for people still reading…I’m sure you know that you can reverse a sort order simply by passing in sorted(items, reverse=True, ...) but what if you want to have different directions depend on the key that you’re sorting on.

Using the technique of a lambda function that returns a tuple, here’s how we sort a slightly more advanced structure:

>>> peeps = [{'name': 'Bill', 'salary': 1000}, {'name': 'Bill', 'salary': 500}, {'name': 'Ted', 'salary': 500}]

And now, sort with a lambda function returning a tuple:

>>> sorted(peeps, key=lambda x: (x['name'], x['salary']))[{'salary': 500, 'name': 'Bill'}, {'salary': 1000, 'name': 'Bill'}, {'salary': 500, 'name': 'Ted'}]

Makes sense, right? Bill comes before Ted and 500 comes before 1000. But how do you sort it like that on the name but reverse on the salary? Simple, negate it:

>>> sorted(peeps, key=lambda x: (x['name'], -x['salary']))[{'salary': 1000, 'name': 'Bill'}, {'salary': 500, 'name': 'Bill'}, {'salary': 500, 'name': 'Ted'}]
In Python you sort with a tuple

相关文章:

你感兴趣的文章:

标签云: