# Sorting list based on values from another list

Posted on

Solving problem is about exposing yourself to as many situations as possible like Sorting list based on values from another list and practice these strategies over and over. With time, it becomes second nature and a natural way you approach any problems in general. Big or small, always start with a plan, use other strategies mentioned here till you are confident and ready to code the solution.
In this post, my aim is to share an overview the topic about Sorting list based on values from another list, which can be followed any time. Take easy to follow this discuss.

Sorting list based on values from another list

I have a list of strings like this:

``````X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,   0,   1,   2,   2,   0,   1 ]
``````

What is the shortest way of sorting X using values from Y to get the following output?

``````["a", "d", "h", "b", "c", "e", "i", "f", "g"]
``````

The order of the elements having the same “key” does not matter. I can resort to the use of `for` constructs but I am curious if there is a shorter way. Any suggestions?

Shortest Code

``````[x for _,x in sorted(zip(Y,X))]
``````

Example:

``````X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
Z = [x for _,x in sorted(zip(Y,X))]
print(Z)  # ["a", "d", "h", "b", "c", "e", "i", "f", "g"]
``````

Generally Speaking

``````[x for _, x in sorted(zip(Y,X), key=lambda pair: pair[0])]
``````

Explained:

1. `zip` the two `list`s.
2. create a new, sorted `list` based on the `zip` using `sorted()`.
3. using a list comprehension extract the first elements of each pair from the sorted, zipped `list`.

For more information on how to setuse the `key` parameter as well as the `sorted` function in general, take a look at this.

Zip the two lists together, sort it, then take the parts you want:

``````>>> yx = zip(Y, X)
>>> yx
[(0, 'a'), (1, 'b'), (1, 'c'), (0, 'd'), (1, 'e'), (2, 'f'), (2, 'g'), (0, 'h'), (1, 'i')]
>>> yx.sort()
>>> yx
[(0, 'a'), (0, 'd'), (0, 'h'), (1, 'b'), (1, 'c'), (1, 'e'), (1, 'i'), (2, 'f'), (2, 'g')]
>>> x_sorted = [x for y, x in yx]
>>> x_sorted
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']
``````

Combine these together to get:

``````[x for y, x in sorted(zip(Y, X))]
``````

Also, if you don’t mind using numpy arrays (or in fact already are dealing with numpy arrays…), here is another nice solution:

``````people = ['Jim', 'Pam', 'Micheal', 'Dwight']
ages = [27, 25, 4, 9]
import numpy
people = numpy.array(people)
ages = numpy.array(ages)
inds = ages.argsort()
sortedPeople = people[inds]
``````

I found it here:
http://scienceoss.com/sort-one-list-by-another-list/

The most obvious solution to me is to use the `key` keyword arg.

``````>>> X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
>>> Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
>>> keydict = dict(zip(X, Y))
>>> X.sort(key=keydict.get)
>>> X
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']
``````

Note that you can shorten this to a one-liner if you care to:

``````>>> X.sort(key=dict(zip(X, Y)).get)
``````

As Wenmin Mu and Jack Peng have pointed out, this assumes that the values in `X` are all distinct. That’s easily managed with an index list:

``````>>> Z = ["A", "A", "C", "C", "C", "F", "G", "H", "I"]
>>> Z_index = list(range(len(Z)))
>>> Z_index.sort(key=keydict.get)
>>> Z = [Z[i] for i in Z_index]
>>> Z
['A', 'C', 'H', 'A', 'C', 'C', 'I', 'F', 'G']
``````

Since the decorate-sort-undecorate approach described by Whatang is a little simpler and works in all cases, it’s probably better most of the time. (This is a very old answer!)

`more_itertools` has a tool for sorting iterables in parallel:

Given

``````from more_itertools import sort_together
X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
``````

Demo

``````sort_together([Y, X])[1]
# ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
``````

I actually came here looking to sort a list by a list where the values matched.

``````list_a = ['foo', 'bar', 'baz']
list_b = ['baz', 'bar', 'foo']
sorted(list_b, key=lambda x: list_a.index(x))
# ['foo', 'bar', 'baz']
``````

I like having a list of sorted indices. That way, I can sort any list in the same order as the source list. Once you have a list of sorted indices, a simple list comprehension will do the trick:

``````X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
sorted_y_idx_list = sorted(range(len(Y)),key=lambda x:Y[x])
Xs = [X[i] for i in sorted_y_idx_list ]
print( "Xs:", Xs )
# prints: Xs: ["a", "d", "h", "b", "c", "e", "i", "f", "g"]
``````

Note that the sorted index list can also be gotten using `numpy.argsort()`.

``````zip(*sorted(zip(Y,X)))[1]
``````list(zip(*sorted(zip(B,A))))[1]