sort by last then by first

Andrew Henshaw andrew.henshaw at gtri.gatech.edu
Tue Jan 28 08:42:48 EST 2003


<posted & mailed>

Alex Martelli wrote:

> Jørgen Cederberg wrote:
>    ...
>> When sorting a list by several fields, I use this method:
>> 
>>  >>> names=[['Flintstone', 'Fred'],['Flintstone', 'Wilma'],['Rubble',
>> ... 'Barney'],['Rubble', 'Betty'],['Flintstone', 'Pebbles']]
>>  >>> snames = [ (x[1], x[0], x) for x in names]
>>  >>> snames.sort()
>>  >>> sortednames = [x[-1] for x in snames]
>>  >>> sortednames
>> [['Rubble', 'Barney'], ['Rubble', 'Betty'], ['Flintstone', 'Fred'],
>> ['Flintstone
>> ', 'Pebbles'], ['Flintstone', 'Wilma']]
>>  >>>
>> 
>> I create a list of tuples, where the tuple is ordered in the way I want
>> to sort, i.e. "sort by last then by first". The last element is the
>> original item to be sorted. Sort them, and then extract the original
>> item.
>> 
>> Maybe there are faster and easier ways, but I understand this one.
> 
> There is no substantially faster way than this approach (the "Decorate,
> Sort, Undecorate" idiom, aka DSU) for long-enough lists.  There _are_
> several slight variants that can come in handy sometimes, e.g. inserting
> in each item of sortednames the index into names of the corresponding
> item, in order to make the sort "stable" (items that compare equal on
> all compared fields are then guaranteed to stay in the same order as
> they were originally -- the sort method, per se, is not stable up to
> Python 2.2, and is not guaranteed to remain stable in the future even
> though it is in 2.3).
> 
> I entirely agree that, almost always, DSU is simpler as well as faster
> (when it counts, i.e., for large lists) than the alternatives.  The
> main exceptions come when the sort needs to be ascending on certain
> fields but descending on others -- _then_ DSU gets a bit hairier.
> 
> 
> Alex

The following is an approach that I sometimes use for complicated sorting.  
While certainly slower than DSU, it is very convenient.  This is a modified 
version of the code that I submitted to the Python Cookbook.

Andy

#################

class Sortable:
    sortorder = []
    def __init__(self, **args):
        self.__dict__.update(args)

    def __cmp__(self, other):
        s=[]
        o=[]
        for attr in Sortable.sortorder:
            if attr[0] == '-':
                attr = attr[1:]
                s.append(getattr(other, attr))
                o.append(getattr(self, attr))
            else:
                s.append(getattr(self, attr))
                o.append(getattr(other, attr))
        return cmp(s,o)

    def __str__(self):
        return '\t'.join(['%s: %s' % x for x in self.__dict__.items()])
    

def test():
    print
    p1=Sortable(dob='900420', gender='f', experience=1)
    p2=Sortable(dob='900410', gender='f', experience=1)
    p3=Sortable(dob='900425', gender='m', experience=1)
    p4=Sortable(dob='900425', gender='m', experience=0)
    l=[p1,p2,p3,p4]

    for order in [('-gender', 'dob'),
                  ('dob', 'experience'),
                  ('-experience', 'dob')]:
        print order
        Sortable.sortorder = order
        l.sort()
        for i in l:  
            print i


if __name__ == '__main__':
    test()

>>> 
('-gender', 'dob')
dob: 900425     gender: m       experience: 1
dob: 900425     gender: m       experience: 0
dob: 900410     gender: f       experience: 1
dob: 900420     gender: f       experience: 1
('dob', 'experience')
dob: 900410     gender: f       experience: 1
dob: 900420     gender: f       experience: 1
dob: 900425     gender: m       experience: 0
dob: 900425     gender: m       experience: 1
('-experience', 'dob')
dob: 900410     gender: f       experience: 1
dob: 900420     gender: f       experience: 1
dob: 900425     gender: m       experience: 1
dob: 900425     gender: m       experience: 0






More information about the Python-list mailing list