Sorting a list of objects by multiple attributes

S

Steve Bergman

Hi,

I am trying to come up with a clean and simple way to sort a list of
objects (in this case SQLObject instances) by multiple attributes.

e.g. a Person object may have an age, a lastName, and a firstName.

I'd like to be able to arbitrarily sort by any combination of those in
any order.

I would especially like it to be clear and simple, since I am
converting a web app from using SQL to using an ORM only and so the
sorting will actually be controlled by variables coming from SELECTs in
a web form. In SQL this is easy enough to accomplish, and I can do it
in python with L.sort(key=lambda i: i.whatever). But for the multiple
attribute case, I'm at a bit of a loss.

Thanks for any help.

-Steve
 
G

gry

For multiple keys the form is quite analogous:

L.sort(key=lambda i: (i.whatever, i.someother, i.anotherkey))

I.e., just return a tuple with the keys in order from your lambda.
Such tuples sort nicely.

-- George Young
 
N

nghoffma

If you are lambda-phobic (as I am) this should also work for an
arbitrary set of attributes:

attrs = 'attr1 attr2 attr3'.split()
sortlist = [[getattr(o,a) for a in attrs] + [o] for o in objects]
sorted_objects = [x[-1] for x in sorted(sortlist)]

-Noah
 
K

Kent Johnson

For multiple keys the form is quite analogous:

L.sort(key=lambda i: (i.whatever, i.someother, i.anotherkey))

I.e., just return a tuple with the keys in order from your lambda.
Such tuples sort nicely.

In Python 2.5 you can do this with operator.attrgetter():

L.sort(key=operator.attrgetter('whatever', 'someother', 'anotherkey'))

Kent
 
S

Scott David Daniels

Kent said:
In Python 2.5 you can do this with operator.attrgetter():
L.sort(key=operator.attrgetter('whatever', 'someother', 'anotherkey'))

Note: this is also available in Python 2.4

--Scott David Daniels
(e-mail address removed)
 
K

Kent Johnson

Scott said:
Note: this is also available in Python 2.4

No, the ability to specify more than one attribute name, making a getter
that returns a tuple, is a Python 2.5 enhancement. In 2.4:

In [1]: import operator

In [2]: operator.attrgetter('whatever', 'someother', 'anotherkey')
------------------------------------------------------------
Traceback (most recent call last):
File "<ipython console>", line 1, in ?
TypeError: attrgetter expected 1 arguments, got 3

Kent
 
R

Raymond Hettinger

[George Young]
For multiple keys the form is quite analogous:

L.sort(key=lambda i: (i.whatever, i.someother, i.anotherkey))
[Noah]
If you are lambda-phobic (as I am) this should also work for an
arbitrary set of attributes:

attrs = 'attr1 attr2 attr3'.split()
sortlist = [[getattr(o,a) for a in attrs] + [o] for o in objects]
sorted_objects = [x[-1] for x in sorted(sortlist)]

The cult of lambda avoidance has lost contact with reality. Some
Pythonistas now habitually twist their code into knots rather than use
lambda. The above code fragment is a case in point -- it is shocking
that the poster deems the three-line rewrite as an improvement on
George's clear and succinct code fragment.

Lambda avoidance is rooted in two things, an aversion to the keyword
name and an observation that misuse can result in atrocious code. Also,
some of the use cases have fallen by the wayside with the introduction
of listcomps, genexps, and operator.attrgetter. Still, some use cases
remain and there is no reason to mangle your code in the name of a
foolish psuedo-principle.

My advice: use lambda when appropriate and don't feel guilty about it
 
S

Scott David Daniels

Kent said:
No, the ability to specify more than one attribute name, making a getter
that returns a tuple, is a Python 2.5 enhancement.

Yup, sorry about that. When I installed 2.5a1 the shortcuts I used for
2.4 got switched w/o my noticing (I did try it first). I have now made
my 2.4 shortcuts more explicit (so the next time I'll do this stupid
thing is likely on 2.6).
 
N

nghoffma

I'm sure my avoidance of lambdas as due as much to laziness as
adherence to principle. This is a good opportunity to learn about them.


I suggested the above because it wasn't obvious to me how one would
pass the arbitrary set of attributes to the lambda expression (and I
envisioned them being specified as strings in this case, since the set
of attributes will be coming from a web form).

So what about the following (attrgetter aside)?

attrs = 'attr1 attr2 attr3'.split()
L.sort(key=lambda i: [getattr(i,a) for a in attrs])

-Noah
 
A

Azolex

Raymond said:
The cult of lambda avoidance has lost contact with reality. [...]
Lambda avoidance is rooted in two things, an aversion to the keyword
name [...]

Let's push the diagnosis a bit further : the aversion to the keyword
"lambda" has to do with the fact that it ignores the english word used
by all non-geeks to convey the meaning, eg "given"
 
R

Raymond Hettinger

Azolex:
Let's push the diagnosis a bit further : the aversion to the keyword
"lambda" has to do with the fact that it ignores the english word used
by all non-geeks to convey the meaning, eg "given"

Right. However, Guido has said that lambda is here to stay,
so it's time to get over it.


Raymond
 
R

Raymond Hettinger

[Noah]
I suggested the above because it wasn't obvious to me how one would
pass the arbitrary set of attributes to the lambda expression (and I
envisioned them being specified as strings in this case, since the set
of attributes will be coming from a web form).

So what about the following (attrgetter aside)?

attrs = 'attr1 attr2 attr3'.split()
L.sort(key=lambda i: [getattr(i,a) for a in attrs])

When starting with attribute names in a list of strings,
this code looks fine. The Py2.5 version of attrgetter
will be even cleaner:

L.sort(key=attrgetter(*attrs))
 
A

Azolex

Raymond said:
Azolex:

Right. However, Guido has said that lambda is here to stay,
so it's time to get over it.

You are saying lambda is a given ? ;)

I've not observed the BDFL's pronouncement, so I have to ask : was it
clear from his words that he meant the actual keyword, or could it be he
just meant the construct while refering to it by the keyword ?
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top