22

I have a python dictionary setup like so

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

I need to write a function which returns the ordered keys in a list, depending on which column your sorting on so for example if we're sorting on mydict[key][1] (ascending)

I should receive a list back like so

['a2', 'a4', 'a3', 'a1', 'a6', 'a5']

It mostly works, apart from when you have columns of the same value for multiple keys, eg. 'a2': ['e',2] and 'a4': ['s',2]. In this instance it returns the list like so

['a4', 'a4', 'a3', 'a1', 'a6', 'a5']

Here's the function I've defined

def itlist(table_dict,column_nb,order="A"):
    try:
        keys = table_dict.keys()
        values = [i[column_nb-1] for i in table_dict.values()]
        combo = zip(values,keys)
        valkeys = dict(combo)
        sortedCols = sorted(values) if order=="A" else sorted(values,reverse=True)
        sortedKeys = [valkeys[i] for i in sortedCols]
    except (KeyError, IndexError), e:
        pass
    return sortedKeys

And if I want to sort on the numbers column for example it is called like so

sortedkeysasc = itmethods.itlist(table,2)

So any suggestions?

Paul

ulidtko
  • 14,740
  • 10
  • 56
  • 88
PDStat
  • 5,513
  • 10
  • 51
  • 86

5 Answers5

56

Wouldn't it be much easier to use

sorted(d, key=lambda k: d[k][1])

(with d being the dictionary)?

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Yeah, and the iterator's laziness is particularly an advantage too. This solution is better than mine, pity that I can't upvote twice %) – ulidtko Jan 14 '11 at 21:57
  • @ulidtko: The lazines isn't particularly relevant, since `sorted()` will generate the whole list before sorting anyway. It would be equivalent to use `a = d.keys(); a.sort(key=lambda k: d[k][1])` here, but `sorted(d.keys(), ...)` would create a redundant copy of the list. – Sven Marnach Jan 15 '11 at 01:08
  • This is neat. Is it possible to do the reverse i.e. sort dictionary values based on keys & return list of value only in 1 line. Basically avoid list comprehension in this `[v for (k,v) in sorted(d.iteritems(), key=lambda (k, v) : v ) ]` – user May 20 '14 at 17:26
  • @buffer: You can't do the very same thing since you can't look up the key given the value. Your approach is fine in my opinion (possibly with `key=operator.itemgetter(1)` instead). Alternatives would be `map(d.get, sorted(d))` or the rather non-obvious `itemgetter(*sorted(d))(d)`. – Sven Marnach May 20 '14 at 17:47
  • 1
    @buffer: I just noticed that your code actually sorts by value, not by key, as you say in the text. If this is what you actually need, you don't look at the keys at all, so simply `sorted(d.itervalues())` does the trick. – Sven Marnach May 20 '14 at 17:51
  • My bad. It should have been `[v for (k,v) in sorted(d.iteritems(), key=lambda (k, v) : k ) ]` – user May 21 '14 at 03:54
  • @Sven When I try this code (Python 2.7) I get `TypeError: 'int' object has no attribute '__getitem__'`, b/c I have a dictionary without lists as values, but ints. So I use `sorted(d.iterkeys(), key=lambda k: d[k])` – eric Feb 28 '15 at 18:46
  • @neuronet: To sort a different data structure than the one in the question, you of course need to adapt the code. I'd suggest the even more succinct `sorted(d, key=d.get)` for your specific use case. – Sven Marnach Mar 02 '15 at 14:12
10
>>> L = sorted(d.items(), key=lambda (k, v): v[1])
>>> L
[('a2', ['e', 2]), ('a4', ['s', 2]), ('a3', ['h', 3]), ('a1', ['g', 6]), ('a6', ['y', 7]), ('a5', ['j', 9])]

>>> map(lambda (k,v): k, L)
['a2', 'a4', 'a3', 'a1', 'a6', 'a5']

Here you sort the dictionary items (key-value pairs) using a key - callable which establishes a total order on the items.

Then, you just filter out needed values using a map with a lambda which just selects the key. So you get the needed list of keys.


EDIT: see this answer for a much better solution.

Community
  • 1
  • 1
ulidtko
  • 14,740
  • 10
  • 56
  • 88
3

Although there are multiple working answers above, a slight variation / combination of them is the most pythonic to me:

[k for (k,v) in sorted(mydict.items(), key=lambda (k, v): v[1])]
Kimvais
  • 38,306
  • 16
  • 108
  • 142
2
>>> mydict = { 'a1': ['g',6],
...            'a2': ['e',2],
...            'a3': ['h',3],
...            'a4': ['s',2],
...            'a5': ['j',9],
...            'a6': ['y',7] }
>>> sorted(mydict, key=lambda k:mydict[k][1])
['a2', 'a4', 'a3', 'a1', 'a6', 'a5']
>>> sorted(mydict, key=lambda k:mydict[k][0])
['a2', 'a1', 'a3', 'a5', 'a4', 'a6']
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
0
def itlist(table_dict, col, desc=False):
    return [key for (key,val) in
        sorted(
            table_dict.iteritems(),
            key=lambda x:x[1][col-1],
            reverese=desc,
            )
        ]
Amber
  • 507,862
  • 82
  • 626
  • 550