Is there an easy way to sort a list by two criteria?

thebjorn BjornSteinarFjeldPettersen at gmail.com
Sat Feb 9 22:35:35 EST 2008


On Feb 10, 3:05 am, neocortex <pmi... at gmail.com> wrote:
> Hello!
> I am a newbie in Python. Recently, I get stuck with the problem of
> sorting by two criteria. In brief, I have a two-dimensional list (for
> a table or a matrix). Now, I need to sort by two columns, but I cannot
> figure out how to do that. I read somewhere that it is possible to do:>>> table.sort().sort()
>
> but it does not work.
> Can anyone help me with this?
> PS: I am using Python under Ubuntu 6.06.
>
> Best,
> PM

I'm not sure which Python is default for Ubuntu 6.06, but assuming you
can access a recent one (2.4), the list.sort() function takes a key
argument (that seems to be rather sparsely documented in the tutorial
and the docstring...). E.g.:

>>> lst = [(1,2,4),(3,2,1),(2,2,2),(2,1,4),(2,4,1)]
>>> lst.sort(key=lambda (a,b,c):(c,b))
>>> lst
[(3, 2, 1), (2, 4, 1), (2, 2, 2), (2, 1, 4), (1, 2, 4)]
>>>

The "fancy" lambda simply takes a source-tuple and returns a tuple of
the keys to be sorted on, in this case sort on the last element, then
on the middle element.

You can use the cmp argument to get similar results (with the same
list as above):

>>> lst.sort(cmp=lambda (a1,a2,a3),(b1,b2,b3):cmp((a3,a2),(b3,b2)))
>>> lst
[(3, 2, 1), (2, 4, 1), (2, 2, 2), (2, 1, 4), (1, 2, 4)]

The lambda in this case is starting to get complicated though, so
probably better to write a separate function. Using the cmp argument
is slower than using the key argument since key is only called once
for each element in the list while cmp is called for each comparison
of elements (it's not the number of times the function is called
that's the big deal, but rather that the highly optimized sort needs
to continually call back to Python in the cmp case).

The old-school way of doing this is using a Schwartzian transform
(a.k.a. decorate-sort-undecorate) which creates an auxilliary list
with the keys in correct sorting order so that sort() can work
directly:

>>> lst
[(1, 2, 4), (3, 2, 1), (2, 2, 2), (2, 1, 4), (2, 4, 1)]
>>> decorate = [(x[2],x[1],x) for x in lst]
>>> decorate.sort()
>>> decorate
[(1, 2, (3, 2, 1)), (1, 4, (2, 4, 1)), (2, 2, (2, 2, 2)), (4, 1, (2,
1, 4)), (4, 2, (1, 2, 4))]
>>> lst = [x[2] for x in decorate]  # undecorate
>>> lst
[(3, 2, 1), (2, 4, 1), (2, 2, 2), (2, 1, 4), (1, 2, 4)]
>>>

hth,
-- bjorn



More information about the Python-list mailing list