How do I sort these?

K

KraftDiner

I have two lists.
I want to sort by a value in the first list and have the second list
sorted as well... Any suggestions on how I should/could do this?
 
S

Steve Holden

KraftDiner said:
I have two lists.
I want to sort by a value in the first list and have the second list
sorted as well... Any suggestions on how I should/could do this?
>>> first = [1, 3, 5, 7, 9, 2, 4, 6, 8]
>>> second = ['one', 'three', 'five', 'seven', 'nine', 'two', 'four', 'six', 'eight']
>>> both = zip(first, second)
>>> both.sort()
>>> [b[0] for b in both] [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [b[1] for b in both] ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
>>>

You mean like this?

regards
Steve
 
D

Diez B. Roggisch

KraftDiner said:
I have two lists.
I want to sort by a value in the first list and have the second list
sorted as well... Any suggestions on how I should/could do this?

I guess you mean that you have two lists of same size where each index
position pointing to corrsponding items - like two excel columns? Then
this helps:

sl = zip(list_a, list_b)
sl.sort()
list_a, list_b = unzip(*sl)

Regards,

Diez
 
K

KraftDiner

In C++ you can specify a comparision method, how can I do this with
python...
Say for instance the first list was a dictionary and I wanted to sort
on one of the keys in the dictionary?
 
P

Paul Rubin

KraftDiner said:
In C++ you can specify a comparision method, how can I do this with
python...

Yes, see the docs. Just pass a comparison func to the sort method.
 
S

Sam Pointon

unzip doesn't seem to work for me...

It's not a part of the standard Python distribution, but this is a
naive implementation (it doesn't react well to the list's contents
having different lengths).

def unzip(seq):
result = [[] for i in range(len(seq[0]))]
for item in seq:
for index in range(len(item)):
result[index].append(item[index])
return result
[[0, 1, 2, 3, 4], ['a', 'b', 'c', 'd', 'e']]
 
A

Amaury

Sam Pointon a écrit :
unzip doesn't seem to work for me...


It's not a part of the standard Python distribution, but this is a
naive implementation (it doesn't react well to the list's contents
having different lengths).

def unzip(seq):
result = [[] for i in range(len(seq[0]))]
for item in seq:
for index in range(len(item)):
result[index].append(item[index])
return result


[[0, 1, 2, 3, 4], ['a', 'b', 'c', 'd', 'e']]

unzip() could be also be written like this:

def unzip(seq):
return zip(*seq)

Faster and nicer, isn't it?

Except that it returns a list of tuples:[(0, 1, 2, 3, 4), ('a', 'b', 'c', 'd', 'e')]
 
S

Steven D'Aprano

unzip doesn't seem to work for me...

Nor for me:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'unzip' is not defined


I don't think it exists.

But it is easy enough to create:

def unzip(L):
"""Reverse a zip.

Expects L to be a list of the form:
[(1, 'a'), (2, 'b'), (3, 'c')]
and returns:
[(1, 2, 3), ('a', 'b', 'c')]

Note that unzip(zip(seq1, seq2)) is not quite a null-op,
because some type information is lost, e.g. lists and
strings are converted into tuples.
"""
return zip(*L)

As you can see, the documentation for unzip is longer than the code itself :)
 
A

Alex Martelli

Paul Rubin said:
Yes, see the docs. Just pass a comparison func to the sort method.

Or, better, pass a key-extraction function, that's much handier and
faster (it automates the "decorate-sort-undecorate", DSU, idiom).


Alex
 
D

Diez B. Roggisch

KraftDiner said:
unzip doesn't seem to work for me...

Mrmpf, my bad. The operation is called unzip, but implpemented by using
zip - so


unzip(l) == zip(*l)


So the exchange unzip with zip in my example.

Diez
 
B

Bengt Richter

KraftDiner said:
I have two lists.
I want to sort by a value in the first list and have the second list
sorted as well... Any suggestions on how I should/could do this?
first = [1, 3, 5, 7, 9, 2, 4, 6, 8]
second = ['one', 'three', 'five', 'seven', 'nine', 'two', 'four', 'six', 'eight']
both = zip(first, second)
both.sort()
[b[0] for b in both] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[b[1] for b in both] ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']

You mean like this?
ISTM there could be a subtle requirement in the way the OP stated what he wanted to do.
I.e., it sounds like he would like to sort the first list and have a second list undergo
the same shuffling as was required to sort the first. That's different from having the
data of the second participate in the sort as order-determining data, if equals in the
first list are not to be re-ordered:
>>> first = [2]*5 + [1]*5
>>> first [2, 2, 2, 2, 2, 1, 1, 1, 1, 1]
>>> sorted(first) [1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
>>> second = [chr(ord('A')+i) for i in xrange(9,-1,-1)]
>>> second ['J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']
>>> sorted(second)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']

Now the zipped sort unzipped: [(1, 1, 1, 1, 1, 2, 2, 2, 2, 2), ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J')]

Now suppose we sort the first and use the elements' indices to preserve order where equal [(1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4)]

Separate out the first list elements:
>>> [t[0] for t in sorted((f,i) for i,f in enumerate(first))]
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2]

Now select from the second list, by first-element position correspondence:
>>> [second[t[1]] for t in sorted((f,i) for i,f in enumerate(first))]
['E', 'D', 'C', 'B', 'A', 'J', 'I', 'H', 'G', 'F']

Which did the OP really want? ;-)

Regards,
Bengt Richter
 
B

bearophileHUGS

# This can be a solution:

from operator import itemgetter
seq1a = ([2] * 4) + ([1] * 4)
seq2a = [complex(3-el,7-el) for el in range(1, 9)]
assert len(seq1a) == len(seq2a)
print seq1a, seq2a, "\n"

mix = zip(seq1a, seq2a)
# mix.sort() # Not possible
mix.sort(key=itemgetter(0))

# If you need tuples output:
seq1b, seq2b = zip(*mix)
print seq1b, seq2b, "\n"

# Alternative, lists output:
seq1b = [el[0] for el in mix]
seq2b = [el[1] for el in mix]
print seq1b, seq2b, "\n"

Bye,
bearophile
 
P

Peter Otten

Bengt said:
KraftDiner said:
I have two lists.
I want to sort by a value in the first list and have the second list
sorted as well... Any suggestions on how I should/could do this?

first = [1, 3, 5, 7, 9, 2, 4, 6, 8]
second = ['one', 'three', 'five', 'seven', 'nine', 'two', 'four', 'six', 'eight']
both = zip(first, second)
both.sort()
[b[0] for b in both] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[b[1] for b in both]
['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
You mean like this?
ISTM there could be a subtle requirement in the way the OP stated what he
wanted to do. I.e., it sounds like he would like to sort the first list
and have a second list undergo the same shuffling as was required to sort
the first. That's different from having the data of the second participate
in the sort as order-determining data, if equals in the first list are not
to be re-ordered:
first = [2]*5 + [1]*5
first [2, 2, 2, 2, 2, 1, 1, 1, 1, 1]
sorted(first) [1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
second = [chr(ord('A')+i) for i in xrange(9,-1,-1)]
second ['J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']
sorted(second)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']

Now the zipped sort unzipped:[(1, 1, 1, 1, 1, 2, 2, 2, 2, 2), ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
['I', 'J')]

Now suppose we sort the first and use the elements' indices to preserve
order where equal[(1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (2, 0), (2, 1), (2, 2), (2, 3),
[(2, 4)]

Separate out the first list elements:
[t[0] for t in sorted((f,i) for i,f in enumerate(first))]
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2]

Now select from the second list, by first-element position correspondence:
[second[t[1]] for t in sorted((f,i) for i,f in enumerate(first))]
['E', 'D', 'C', 'B', 'A', 'J', 'I', 'H', 'G', 'F']

Which did the OP really want? ;-)

I don't know, but there certainly is no subtle requirement to not provide
the key argument:
import operator
first = [2]*5 + [1]*5
second = list(reversed("ABCDEFGHIJ"))
[s for f, s in sorted(zip(first, second))] ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
[s for f, s in sorted(zip(first, second), key=operator.itemgetter(0))]
['E', 'D', 'C', 'B', 'A', 'J', 'I', 'H', 'G', 'F']

Peter
 
B

Bengt Richter

Bengt Richter wrote:
[...]
Now select from the second list, by first-element position correspondence:
[second[t[1]] for t in sorted((f,i) for i,f in enumerate(first))]
['E', 'D', 'C', 'B', 'A', 'J', 'I', 'H', 'G', 'F']

Which did the OP really want? ;-)

I don't know, but there certainly is no subtle requirement to not provide
the key argument:
import operator
first = [2]*5 + [1]*5
second = list(reversed("ABCDEFGHIJ"))
[s for f, s in sorted(zip(first, second))] ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
[s for f, s in sorted(zip(first, second), key=operator.itemgetter(0))]
['E', 'D', 'C', 'B', 'A', 'J', 'I', 'H', 'G', 'F']
D'oh yeah, forgot about key ;-/ (and this kind of problem probably at least
partly motivated its introduction, so it should have jumped to mind).
Thanks, your version is much cleaner ;-)

Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top