Sorting dictionary using operator.itemgetter

40,121

Solution 1

In [6]: sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
Out[6]: 
[('a2', ['e', 2]),
 ('a4', ['s', 2]),
 ('a3', ['h', 3]),
 ('a1', ['g', 6]),
 ('a6', ['y', 7]),
 ('a5', ['j', 9])]

The key parameter is always a function that is fed one item from the iterable (mydict.iteritems()) at a time. In this case, an item could be something like

('a2',['e',2])

So we need a function that can take ('a2',['e',2]) as input and return 2.

lambda (k,v): ... is an anonymous function which takes one argument -- a 2-tuple -- and unpacks it into k and v. So when the lambda function is applied to our item, k would be 'a2' and v would be ['e',2].

lambda (k,v): operator.itemgetter(1)(v) applied to our item thus returns operator.itemgetter(1)(['e',2]), which "itemgets" the second item in ['e',2], which is 2.

Note that lambda (k,v): operator.itemgetter(1)(v) is not a good way to code in Python. As gnibbler points out, operator.itemgetter(1) is recomputed for each item. That's inefficient. The point of using operator.itemgetter(1) is to create a function that can be applied many times. You don't want to re-create the function each time. lambda (k,v): v[1] is more readable, and faster:

In [15]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): v[1])
100000 loops, best of 3: 7.55 us per loop

In [16]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
100000 loops, best of 3: 11.2 us per loop

Solution 2

The answer is -- you can't. operator.itemgetter(i) returns a callable that returns the item i of its argument, that is

f = operator.itemgetter(i)
f(d) == d[i]

it will never return simething like d[i][j]. If you really want to do this in a purely functional style, you can write your own compose() function:

def compose(f, g):
    return lambda *args: f(g(*args))

and use

sorted(mydict.iteritems(), key=compose(operator.itemgetter(1),
                                       operator.itemgetter(1)))

Note that I did not recommend to do this :)

Solution 3

itemgetter doesn't support nesting ( although attrgetter does)

you'd need to flatten the dict like this

sorted(([k]+v for k,v in mydict.iteritems()), key=itemgetter(2))
Share:
40,121

Related videos on Youtube

user225312
Author by

user225312

Updated on November 25, 2020

Comments

  • user225312
    user225312 over 3 years

    A question was asked here on SO, a few minutes ago, on sorting dictionary keys based on their values.

    I just read about the operator.itemgetter method of sorting a few days back and decided to try that, but it doesn't seem to be working.

    Not that I have any problems with the answers presented to the questions, I just wanted to try this with operator.itemgetter.

    So the dict was:

    >>> mydict = { 'a1': ['g',6],
               'a2': ['e',2],
               'a3': ['h',3],
               'a4': ['s',2],
               'a5': ['j',9],
               'a6': ['y',7] }
    

    I tried this:

    >>> l = sorted(mydict.itervalues(), key=operator.itemgetter(1))
    >>> l
    [['e', 2], ['s', 2], ['h', 3], ['g', 6], ['y', 7], ['j', 9]]
    

    And this works as I want it to. However, since I don't have the complete dictionary (mydict.itervalues()), I tried this:

    >>> complete = sorted(mydict.iteritems(), key=operator.itemgetter(2))
    

    This doesn't work (as I expected it to).

    So how do I sort the dict using operator.itemgetter and call itemgetter on the nested key - value pair.

  • user225312
    user225312 over 13 years
    Can you please explain this: operator.itemgetter(1)(v))?
  • user225312
    user225312 over 13 years
    unutbu has a solution, though it's not clear. We will wait for him to update it.
  • Sven Marnach
    Sven Marnach over 13 years
    @A A: operator.itemgetter(1)(v) is equivalent to v[1]. You can replace just any occurrence of a[b] with operator.itemgetter(b)(a), but this is not how I did understand your question :)
  • John La Rooy
    John La Rooy over 13 years
    Creating an itemgetter is relatively expensive. This creates one for each key in the dict
  • ezdazuzena
    ezdazuzena over 10 years
    for python3 use items instead of iteritems
  • Zelphir Kaltstahl
    Zelphir Kaltstahl about 6 years
    Invalid syntax anyway, because lambda does not take the tuple.
  • Mateen Ulhaq
    Mateen Ulhaq over 5 years
    I think it would be valuable to put a disclaimer at the top lest the naive Google searcher unwittingly think's it's cool to use that