How to Sort Two Lists (Which Reference Each Other) in the Exact Same Way

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.

Sort two lists the same way

The easiest solution would be to create a struct/class to combine both variables so you don't have to worry about keeping the objects in the arrays aligned. The last thing you need to do is to sort the array ob new objects by the date. For that, I cannot help you due to missing knowledge about Dart.

How to sort two lists based on one first and the other next in Python?

Pair, sort, unpair, write back:

list_a[:], list_b[:] = zip(*sorted(zip(list_a, list_b), key=lambda p: (-p[0], p[1])))

Writing back keeps it short and sorts the existing list objects rather than having separate new ones.

More vertical version, probably nicer:

pairs = list(zip(list_a, list_b))
pairs.sort(key=lambda p: (-p[0], p[1]))
list_a[:], list_b[:] = zip(*pairs)

How to sort two lists that correspond?

Can you try the following:

data = list(zip(names, ages))
data.sort(key=lambda x: x[1], reverse=True)
for val in data:
print(val[0], val[1])

Sort or remove elements from corresponding list in same way as in reference list

Try this:

[i for j, i in sorted(zip(listA, listB))]

Output:

listA = [7, 6, 3, 2, 1, 4, 5]
listB = ["a", "b", "c", "d", "e", "f", "g"]

In [5]: [i for j, i in sorted(zip(listA, listB))]
Out[5]: ['e', 'd', 'c', 'f', 'g', 'b', 'a']

for supporting C and D (removing duplicates):

sorted(list({j: i for j, i in reversed(sorted(zip(listC, listD)))}.values()))

.values() returns ListD:['a', 'c', 'e', 'f', 'g'] and .keys() returns ListC:[1, 4, 5, 6, 7]

Python sorting two lists together?

the only issue with your code is the print part (and you'd have to reverse the sort):

scores, names = (list(t) for t in zip(*sorted(zip(scores, names), reverse=True)))

for name, score, _i in zip(scores, names, range(3)):
print(name, score)

# 450 Spec
# 300 Meng
# 200 Sparc

i'd suggest you use collections.Counter:

from collections import Counter

names = ['Xander', 'Spec', 'Meng', 'Sparc', 'Jones', 'Nick', 'Link']
scores = [120, 450, 300, 200, 66, 183, 80]

counter = Counter({name: score for name, score in zip(names, scores)})
print(counter.most_common(3))
# [('Spec', 450), ('Meng', 300), ('Sparc', 200)]

you could then format the result the way you prefer.

order two list with the same indices

This is exactly the sort of task for which lsort has the -indices option (requires 8.5 or later). Instead of the list of sorted values, it returns the list of indices into the original list in the order that that you would retrieve them to get a sorted list, and that's perfect for your task. This interactive session (in Tcl 8.6 so I have lmap) is indicative:

% set listA {5 6 7 3 4 7 8 9}
5 6 7 3 4 7 8 9
% set listB {0 1 2 3 4 5 6 7}
0 1 2 3 4 5 6 7
% set idxs [lsort -indices $listA]
3 4 0 1 2 5 6 7
% lmap i $idxs {lindex $listA $i}
3 4 5 6 7 7 8 9
% lmap i $idxs {lindex $listB $i}
3 4 0 1 2 5 6 7

If you're still on 8.5, you can use foreach to do the remapping; it's easier with a procedure:

proc mapindices {indexlist valuelist} {
set result {}
foreach i $indexlist {
lappend result [lindex $valuelist $i]
}
return $result
}
set listA {5 6 7 3 4 7 8 9}
set listB {0 1 2 3 4 5 6 7}
puts [mapindices [lsort -indices $listA] $listB]

Now, in 8.4 (NO LONGER SUPPORTED!) there's no indices option so you need to do more work.

proc lsortIndices {list} {
# Pair each value up with its index
set zipped {}
set idx -1
foreach val $list {
lappend zipped [list [incr idx] $val]
}

# Do the sorting by the value of the pair
set sorted [lsort -index 1 $zipped]

# Unpack the indices from the pairs to get the result
set result {}
foreach pair $sorted {
lappend result [lindex $pair 0]
}
return $result
}

However, at that point you'd probably just zip the two lists together and work more directly:

set zipped {}
foreach valA $listA valB $listB {
lappend zipped [list $valA $valB]
}

set sorted [lsort -index 0 $zipped]

set listAsorted {}
set listBsorted {}
foreach pair $sorted {
lappend listAsorted [lindex $pair 0]
lappend listBsorted [lindex $pair 1]
}

Working with older versions of Tcl than 8.4 would require you to use the -command option and that's really slow.

Sort 2 lists of lists according to the following common rule

Try:

A = [[4, 8], [8, 9], [5]]
B = [[2, 4], [1, 3], [1, 2]]

idxs, A = zip(*sorted(enumerate(A), key=lambda k: (len(k[1]), k[1])))
B = [B[i] for i in idxs]

print(list(A))
print(B)

Prints:

[[5], [4, 8], [8, 9]]
[[1, 2], [2, 4], [1, 3]]


Related Topics



Leave a reply



Submit