List Sort Based on Another List

Sorting list based on values from another list

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 lists.
  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 set\use the key parameter as well as the sorted function in general, take a look at this.



Kotlin: Sort list based on another list (different objects)

The easiest approach would be to first create a mapping of the list you want to sort by. It should associate the id to the actual object.

Then you iterate the list you want to sort by and map it's values to the object you retrieved from the mapping you just created.

Here a minimal example:

// both can have more properties of course
data class Foo(val id: Int)
data class Bar(val id: Int)

val fooList = listOf(Foo(4), Foo(2), Foo(1), Foo(3))
val barList = listOf(Bar(1), Bar(2), Bar(3), Bar(4))

val idValueMap = barList.associateBy { it.id }

val sortedByOtherList = fooList.map { idValueMap[it.id] }

println(sortedByOtherList)

Result:

[Bar(id=4), Bar(id=2), Bar(id=1), Bar(id=3)]

How to sort two lists (which reference each other) in the exact same way

One classic approach to this problem is to use the "decorate, sort, undecorate" idiom, which is especially simple using python's built-in zip function:

>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2
('one', 'one2', 'two', 'three', 'four')

These of course are no longer lists, but that's easily remedied, if it matters:

>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']

It's worth noting that the above may sacrifice speed for terseness; the in-place version, which takes up 3 lines, is a tad faster on my machine for small lists:

>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop

On the other hand, for larger lists, the one-line version could be faster:

>>> %timeit zip(*sorted(zip(list1, list2)))
100 loops, best of 3: 8.09 ms per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100 loops, best of 3: 8.51 ms per loop

As Quantum7 points out, JSF's suggestion is a bit faster still, but it will probably only ever be a little bit faster, because Python uses the very same DSU idiom internally for all key-based sorts. It's just happening a little closer to the bare metal. (This shows just how well optimized the zip routines are!)

I think the zip-based approach is more flexible and is a little more readable, so I prefer it.


Note that when elements of list1 are equal, this approach will end up comparing elements of list2. If elements of list2 don't support comparison, or don't produce a boolean when compared (for example, if list2 is a list of NumPy arrays), this will fail, and if elements of list2 are very expensive to compare, it might be better to avoid comparison anyway.

In that case, you can sort indices as suggested in jfs's answer, or you can give the sort a key function that avoids comparing elements of list2:

result1, result2 = zip(*sorted(zip(list1, list2), key=lambda x: x[0]))

Also, the use of zip(*...) as a transpose fails when the input is empty. If your inputs might be empty, you will have to handle that case separately.

How to sort a list by another list

This isn't the most performant way, but it's probably one of the simplest. Use a sorting method that sorts based on the object's field's location in the other array:

final sortedExamples = List.from(examples)
..sort((a, b) => ids.indexOf(a.id) - ids.indexOf(b.id));

This way is slightly more involved but more performant as you only need to iterate over each list once each. It involves making a map out of your list and then using that as the source of truth to rebuild a sorted list:

final ref = Map.fromIterable(examples, key: (e) => e.id, value: (e) => e);
final sortedExamples = List.from(ids.map((id) => ref[id]));

The first option is better if space is an issue, the second is better if speed is an issue.

Sorting list based on another list's order

An efficient solution is to first create the mapping from the ID in the ids (your desired IDs order) to the index in that list:

val orderById = ids.withIndex().associate { (index, it) -> it.id to index }

And then sort your list of people by the order of their id in this mapping:

val sortedPeople = people.sortedBy { orderById[it.id] }

Note: if a person has an ID that is not present in the ids, they will be placed first in the list. To place them last, you can use a nullsLast comparator:

val sortedPeople = people.sortedWith(compareBy(nullsLast<String>()) { orderById[it.id] })

Python 3.x -- sort one list based on another list and then return both lists sorted

Is it possible to merge the last two lines into one line?

Yes it is. ordered_candidates excludes the points from the result, since you only choose the candidates with [x for _, x in...]. This basically only selects the second item from each tuple. Additionally, ordered_points sorts only the points. They both also sort in reverse with reverse=True.

Seems like you can just modify ordered_candidates to include both items in the (point, candidate) pairs.

>>> points = [4,7,3,2,7]
>>> candidates = ['a', 'b', 'c', 'd', 'e']
>>> sorted(zip(points, candidates), reverse=True)
[(7, 'e'), (7, 'b'), (4, 'a'), (3, 'c'), (2, 'd')]

How to sort one list based on another?

I think this answers your question:

>>> [x for x in Ref if x in Input]
>>> [3, 2, 11, 10, 9, 8, 5, 4]

Hope it helps.

UPDATE:
Making Input a set for faster access:

>>> Input_Set = set(Input)
>>> [x for x in Ref if x in Input_Set]
[3, 2, 11, 10, 9, 8, 5, 4]

Sort a list by presence of items in another list

There is no need to actually sort here. You want the elements in a which are in b, in the same order as they were in a; followed by the elements in b which are not in a, in the same order as they were in b.

We can just do this with two filters, using the sets for fast membership tests:

>>> a = ['30', '10', '90', '1111', '17']
>>> b = ['60', '1201', '30', '17', '900']
>>> a_set = set(a)
>>> b_set = set(b)
>>> [*filter(lambda x: x in b_set, a), *filter(lambda x: x not in a_set, b)]
['30', '17', '60', '1201', '900']

Or if you prefer comprehensions:

>>> [*(x for x in a if x in b_set), *(x for x in b if x not in a_set)]
['30', '17', '60', '1201', '900']

Both take linear time, which is better than sorting.



Related Topics



Leave a reply



Submit