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:
zip
the twolist
s.- create a new, sorted
list
based on thezip
usingsorted()
. - 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
A Timeout Occured After 30000Ms Selecting a Server Using Compositeserverselector
Except Has Similar Effect to Distinct
Can You Explain Liskov Substitution Principle with a Good C# Example
Replace Unicode Escape Sequences in a String
Client Is Unauthorized to Retrieve Access Tokens Using This Method Gmail API C#
Delete Specific Line from a Text File
What Is the Best Practice for "Copy Local" and with Project References
How to Avoid "Too Many Parameters" Problem in API Design
Ms Dynamics Crm Online 2011 - Authentication Issues
Equivalence of "With...End With" in C#
Difference Between Delegate.Invoke and Delegate()
System.Web.Httpcontext.Current.User.Identity.Name VS System.Environment.Username in ASP.NET