Gmail Calendar Documents Reader Web more »
Recently Visited Groups | Help | Sign in
Google Groups Home
Message from discussion mini-howto: sorting
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Michael Scharf  
View profile  
 More options Feb 15 1999, 3:00 am
Newsgroups: comp.lang.python
From: Michael Scharf <Michael.Sch...@gmx.de>
Date: 1999/02/15
Subject: Re: mini-howto: sorting

Andrew Dalke wrote:

> I know, it's somewhat silly, but I went through a little tutorial
> here showing a half dozen ways to sort a list with the built-in
> "sort" method.  It was short so I wrote it up in case it is
> deserving of a mini HOWTO.  (Yeah, I still haven't written up the
> exception classes HOWTO ):  I think it's deserving because there's
> not one spot in the manual or Lutz' book talking about the different
> ways.
[snip]
> >>> class CmpAttr:
> >>>     def __init__(self, attr):
> >>>         self.attr = attr
> >>>     def __call__(self, x, y):
> >>>         return cmp(getattr(x, self.attr), getattr(y, self.attr))

[snip]

I would like to add yet another way of sorting: a little
sorting framework using design patterns [1]. A few days
ago, a colleague asked me how to sort tables on different
columns (some of them might be sorted inverse). In a
few minutes I created a little framework in python
(which he then implemented in C++). This is yet another
example of python as prototyping language. But I think,
the framework can also be used in python directly.

Let's take the Spam class:

>>> class Spam:
>>>     def __init__(self, spam, eggs):
>>>         self.spam = spam
>>>         self.eggs = eggs
>>>     def __repr__(self):
>>>         return 'Spam(s=%s,e=%s)' %(repr(self.spam),repr(self.eggs))
>>> a = [Spam(1, 4), Spam(9, 3), Spam(4,6),Spam(4,4)]

If you want to sort on two fields (e.g columns in tables or
two attributes) you might use the composite comparer:

>>> class CmpComposite:
>>>     """Takes a list of compare functions and sorts in that order."""
>>>     def __init__(self,*comparers):
>>>         self.comparers=comparers
>>>     def __call__(self,a,b):
>>>         for cmp in self.comparers:
>>>             c=cmp(a,b)
>>>             if c:
>>>                 return c
>>>         return 0

Now you can sort on Spam.spam, eggs.spam:

>>> a.sort(CmpComposite(CmpAttr('spam'),CmpAttr('eggs')))
>>> print a

[Spam(s=1,e=4), Spam(s=4,e=4), Spam(s=4,e=6), Spam(s=9,e=3)]

Or you can sort on Spam.eggs, Spam.spam:

>>> a.sort(CmpComposite(CmpAttr('eggs'),CmpAttr('spam')))
>>> print a

[Spam(s=9,e=3), Spam(s=1,e=4), Spam(s=4,e=4), Spam(s=4,e=6)

But what if you want to sort inverse on spam? Define a CompInverse:

>>> class CmpInverse:
>>>     """Inverses the effect of a cmp."""
>>>     def __init__(self,cmp):
>>>         self.cmp=cmp
>>>     def __call__(self,a,b):
>>>         return -self.cmp(a,b)

Let's try it:

>>> a.sort(CmpInverse(CmpAttr('spam')))

[Spam(s=9,e=3), Spam(s=4,e=6), Spam(s=4,e=4), Spam(s=1,e=4)

OK, and now sort on eggs and inverse on spam:

>>> a.sort(CmpComposite(CmpAttr('eggs'),CmpInverse(CmpAttr('spam'))))

[Spam(s=9,e=3), Spam(s=4,e=4), Spam(s=1,e=4), Spam(s=4,e=6)]

We can use our little framework to sort tables as well:

>>> class CmpColumn:
>>>     """Sorts on an index of a sequence."""
>>>     def __init__(self,column):
>>>         self.column=column
>>>     def __call__(self,a,b):
>>>         return cmp(a[self.column],b[self.column])

>>> table=[(1,2), (1,3), (1,0), (2,0), (0,3), (3,2)]

Let's now sort our table on column 0 and inverse on column 1:

>>> table.sort(CmpComposite(CmpInverse(CmpColumn(1)),CmpColumn(0)))
>>> print table

[(0, 3), (1, 3), (1, 2), (1, 0), (2, 0), (3, 2)]

In terms of design patterns, we can identify CmpInverse
as a Decorator and CmpComposite as Composite pattern.
CmpColumn and CmpAttr are Strategy patterns. It is now
relatively easy to sort on arbitrary combinations of
attributes. The disadvantage is: it can become quite
slow for big lists.

[1] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides:
    Design Patterns: Elements of Reusable Object-Oriented Software.
    Addison-Wesley Pub Co; ISBN: 0201633612; 1994.
    http://www.amazon.com/exec/obidos/ASIN/0201633612

Michael
--
     ''''\     Michael Scharf
    ` c-@@     TakeFive Software
    `    >     http://www.TakeFive.com
     \_ V      mailto:Michael_Sch...@TakeFive.co.at

[ Comparers.py 2K ]
"""A set of comparer classes."""

class CmpComposite:
    """Takes a list of compare functions and sorts in that order."""
    def __init__(self,*comparers):
        self.comparers=comparers
    def __call__(self,a,b):
        for cmp in self.comparers:
            c=cmp(a,b)
            if c:
                return c
        return 0

class CmpInverse:
    """Inverses the effect of a cmp."""
    def __init__(self,cmp):
        self.cmp=cmp
    def __call__(self,a,b):
        return -self.cmp(a,b)

class CmpColumn:
    """Sorts on an index of a sequence."""
    def __init__(self,column):
        self.column=column
    def __call__(self,a,b):
        return cmp(a[self.column],b[self.column])
class CmpAttr:
    """Sorts on an attribute."""
    def __init__(self, attr):
        self.attr = attr
    def __call__(self, x, y):
        return cmp(getattr(x, self.attr), getattr(y, self.attr))

def test():

    l=[3,5,6,71,2,3,4,5]
    print 'sort inv'
    s=CmpInverse(cmp)
    l.sort(s)
    print ' ',l

    table=[
        (1,2),
        (1,3),
        (1,0),
        (2,0),
        (0,3),
        (3,2)
    ]

    print 'sort l[0], inv l[1]'
    table.sort(CmpComposite(CmpColumn(0),CmpInverse(CmpColumn(1))))
    print ' ',table

    print 'sort inv l[1], l[0]'
    table.sort(CmpComposite(CmpInverse(CmpColumn(1)),CmpColumn(0)))
    print ' ',table

    class Spam:
        def __init__(self, spam, eggs):
            self.spam = spam
            self.eggs = eggs
        def __repr__(self):
            return 'Spam(s=%s,e=%s)' %(repr(self.spam),repr(self.eggs))
    a = [Spam(1, 4), Spam(9, 3), Spam(4,6),Spam(4,4)]

    print "sort spam:"
    a.sort(CmpAttr('spam'))
    print ' ',a

    print "sort inv spam:"
    a.sort(CmpInverse(CmpAttr('spam')))
    print ' ', a

    print "sort spam, eggs:"
    a.sort(CmpComposite(CmpAttr('spam'),CmpAttr('eggs')))
    print ' ',a

    print "sort eggs, spam:"
    a.sort(CmpComposite(CmpAttr('eggs'),CmpAttr('spam')))
    print ' ',a

    print "sort eggs, inv spam:"
    a.sort(CmpComposite(CmpAttr('eggs'),CmpInverse(CmpAttr('spam'))))
    print ' ',a

if __name__=='__main__':
    test()


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google