Differences between revisions 11 and 12
Revision 11 as of 2008-11-15 14:00:57
Size: 1390
Editor: localhost
Comment: converted to 1.6 markup
Revision 12 as of 2009-07-17 18:13:31
Size: 2300
Editor: cpe-72-225-254-9
Comment: Added code for sorting on multiple columns of arbitrary type
Deletions are marked like this. Additions are marked like this.
Line 36: Line 36:
This will sort on arbitrary multiple columns of the dictionary.
{{{
#!python
def multikeysort(items, columns):
    from operator import itemgetter
    comparers = [ ((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) for col in columns]
    def sign(a, b):
        if a < b: return -1
        elif a > b: return 1
        else: return 0
    def comparer(left, right):
        for fn, mult in comparers:
            result = sign(fn(left), fn(right))
            if result:
                return mult * result
        else:
            return 0
    return sorted(items, cmp=comparer)
}}}
You can call it like this:
{{{
>>> result = multikeysort(undecorated, ['key1', 'key2', 'key3'])
}}}
Column names preceded by '-' are sorted in descending order:
{{{
>>> result = multikeysort(undecorated, ['-key1', '-key2', '-key3'])
}}}

Sorting Lists of Dictionaries

Frequently you want to sort a list of dictionaries, based on some particular key.

For example:

   1 a = {"key1": 5 , "key2": 8, "key3": 2}
   2 b = {"key1": 7 , "key2": 4, "key3": 9}
   3 c = {"key1": 6 , "key2": 1, "key3": 1}
   4 undecorated = [a, b, c] # how do you sort this list?

There are many ways to do this. Here's the fastest way to do it, as it avoids using a custom comparison function, instead using builtin comparisons. This is the decorate-sort-undecorate pattern, or the Schwartzian transform if you're coming from Perl.

   1 sort_on = "key2"
   2 decorated = [(dict_[sort_on], dict_) for dict_ in undecorated]
   3 decorated.sort()
   4 result = [dict_ for (key, dict_) in decorated]

(The variable was named dict_ because dict is already a builtin.)

Starting with Py2.4 the list.sort() method provides a key= argument for doing the transform in a single step. The new sorted() built-in function goes a step further and encapsulates making a new sorted list while leaving the original intact. Also, the new operator.itemgetter() function helps by constructing functions for key access:

>>> from operator import itemgetter
>>> result = sorted(undecorated, key=itemgetter('key2'))

This will sort on arbitrary multiple columns of the dictionary.

   1 def multikeysort(items, columns):
   2     from operator import itemgetter
   3     comparers = [ ((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) for col in columns]
   4     def sign(a, b):
   5         if   a < b:  return -1
   6         elif a > b:  return 1
   7         else:        return 0    
   8     def comparer(left, right):
   9         for fn, mult in comparers:
  10             result = sign(fn(left), fn(right))
  11             if result:
  12                 return mult * result
  13         else:
  14             return 0
  15     return sorted(items, cmp=comparer)

You can call it like this:

>>> result = multikeysort(undecorated, ['key1', 'key2', 'key3'])

Column names preceded by '-' are sorted in descending order:

>>> result = multikeysort(undecorated, ['-key1', '-key2', '-key3'])

See Also

SortingListsOfDictionaries (last edited 2010-03-30 19:08:00 by wl-ol-s246-130)

Unable to edit the page? See the FrontPage for instructions.