sort(): Giving third argument?

Alex Martelli aleax at aleax.it
Fri Mar 21 06:06:05 EST 2003


Thomas Guettler wrote:

> Hi!
> 
> If I have a list of IDs and a hash which maps
> each ID to a name:
> 
> ids=[1, 2, 3, 4]
> 
> names={
>   1: "foo",
>   2: "bar",
>   ...}
> 
> I can't do the following:
> 
> def mycmp(a, b):
> return cmp(names[a], names[b])
> 
> ids.sort(mycmp)
> 
> since "name" is unkown in mycmp.
> 
> What's the best solution?

decorate-sort-undecorate, as is almost invariably
the case for sorting.

decorated = [ (names[i], i) for i in ids ]
decorated.sort()
ids[:] = [ i for name, i in decorated ]

the assignment to ids[:] in the undecorate step is
only needed if you do want the sort to be in-place.
DSU is most often faster and simpler than passing
a comparison callable to the sort method.

If you ARE keen on passing a comparison callable
come hell or high water, you can do that in several
ways, as is always the case when you want a callable
built to keep some state.  Simplest is generally a
closure:

def make_comparer(names):
    def comparer(a, b, names=names):
        return cmp(names[a], names[b])
    return comparer

and ids.sort(make_comparer(names))

the "names=names" trick is only _needed_ in very
old versions of Python, by the way.

Only slightly more complicated than a closure is
a class whose instances are callable:

class Comparer:
    def __init__(self, names):
        self.names = names
    def __call__(self, a, b):
        return cmp(self.names[a], self.names[b])

and ids.sort(Comparer(names))


But -- DO consider DSU: it's really a superior approach!


Alex





More information about the Python-list mailing list