526
Comment: sorting by converting to (key,dict), then sorting, then converting back to dict
|
← Revision 13 as of 2010-03-30 19:08:00 ⇥
2180
|
Deletions are marked like this. | Additions are marked like this. |
Line 11: | Line 11: |
a = { "key1":5, "key2":8, "key3":2 } b = { "key1":7, "key2":4, "key3":9 } c = { "key1":6, "key2":1, "key3":1 } l = [ a,b,c ] # how do you sort this list? |
a = {"key1": 5 , "key2": 8, "key3": 2} b = {"key1": 7 , "key2": 4, "key3": 9} c = {"key1": 6 , "key2": 1, "key3": 1} undecorated = [a, b, c] # how do you sort this list? |
Line 17: | Line 17: |
There are many ways to do this; Here's one way I've been doing it: | 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. |
Line 21: | Line 21: |
sort_key = "key2" l = [(d[sort_key],d) for d in l] l.sort() l = [d for (k,d) in l] |
sort_on = "key2" decorated = [(dict_[sort_on], dict_) for dict_ in undecorated] decorated.sort() result = [dict_ for (key, dict_) in decorated] |
Line 26: | Line 26: |
(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. {{{ #!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 comparer(left, right): for fn, mult in comparers: result = cmp(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']) }}} == See Also == * [[HowTo/Sorting]] |
Sorting Lists of Dictionaries
Frequently you want to sort a list of dictionaries, based on some particular key.
For example:
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.
(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 comparer(left, right):
5 for fn, mult in comparers:
6 result = cmp(fn(left), fn(right))
7 if result:
8 return mult * result
9 else:
10 return 0
11 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'])