What is the best way to sort list with custom sorting parameters in Python?

Question:

I have a series of lists that looks like this:

li1 = ['a.1', 'b.9', 'c.8', 'd.1', 'e.2']
li2 = ['a.4', 'b.1', 'c.2', 'd.2', 'e.4']

How can I rearrange the items in each list so that the first item is ‘b.something’? For the example above:

li1 = ['b.9', 'a.1', 'c.8', 'd.1', 'e.2']
li2 = ['b.1', 'a.4', 'c.2', 'd.2', 'e.4']

Maintaining the order after the first item isn’t important. Thanks for the help.

Asked By: drbunsen

||

Answers:

You can use sorted, that accepts a key argument, and returns a list:

>>> li1 = ['a.1', 'b.2', 'c.8']
>>> def k(s):
...     if s.startswith('b.'):
...         return 1
...     else:
...         return 2
...
>>> sorted(li1, key=k)
['b.2', 'a.1', 'c.8']

k shall return something that can be compared between iterable items.

Note: sort change the input in-place and returns nothing, when sorted returns the sorted list and does not modify your list. Both work the same way.

Answered By: Joël

Python’s sorting is stable, so you will maintain the order after the first item regardless.

li1.sort(key=lambda x: not x.startswith('b.'))

rearrange the items in each list so that the first item is ‘b.something’

Maintaining the order after the first item isn’t important.

That isn’t sorting, then. Conceptually, you’re just trying to bring that element to the front.

In other words, you want a list that consists of that element, followed by everything that isn’t that element. Fudging this a little for the case where there are multiple b.somethings, and noting that we don’t care what happens as long as the first element is a b.something, we can rephrase that: a list of every element meeting the condition ("starts with b."), followed by every element not meeting the condition. (This is sometimes called partitioning; see for example std::partition in C++.)

In Python, this is as simple as describing those two list-components with list comprehensions and sticking them together:

[x for x in li if x.startswith('b.')] + [x for x in li if not x.startswith('b.')]

…Or you can pretend that you’re sorting, just with a bunch of elements that really only have two values after the key is applied, and apply the appropriate key, as in Ignacio Vasquez-Abrams’ answer.

Answered By: Karl Knechtel
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.