48

What's the Pythonic way to sort a zipped list?

code :

names = list('datx')
vals  = reversed(list(xrange(len(names))))
zipped = zip(names, vals)

print zipped

The code above prints [('d', 3), ('a', 2), ('t', 1), ('x', 0)]

I want to sort zipped by the values. So ideally it would end up looking like this [('x', 0), ('t', 1), ('a', 2), ('d', 3)].

Stephen
  • 1,607
  • 2
  • 18
  • 40
rectangletangle
  • 50,393
  • 94
  • 205
  • 275
  • The first two answers show off a pet peeve of mine about Python: the presence of both `sorted` and `sort`. – JasonFruit Aug 22 '11 at 01:06
  • 4
    There's good reason for both: `.sort()` sorts a list in-place. And `sorted` works on any iterator, but needs to use additional storage to accomplish the task. – Ned Batchelder Aug 22 '11 at 01:10
  • @JasonFruit: If you don't know the difference, I highly recommend you find out. – Matt Joiner Aug 22 '11 at 02:12
  • @Matt Joiner, Ned Batchelder: I know the difference; I think I've not made myself clear. What bugs me is that sort() is a method, and sorted is a built-in function. It doesn't seem consistent. – JasonFruit Aug 22 '11 at 02:13
  • 1
    @JasonFruit (I know this is from a while back), but `sorted` is consistent with other special python syntax, including `in`, `len`, and `reversed`, which depend upon the `__contains__`, `__len__`, and `__getitem__` + `__len__` respectively (I think `sorted` needs `__getitem__` and `__len__` but I'm not sure). In many ways, it's also similar to the syntax for `[]` which is based on `__setitem__` and `__getitem__`, or `()` which initializes `__call__`. They're builtin functions that translate special internal functions into clear external syntax. – Jeff Tratner Jun 12 '12 at 05:26
  • @JeffTratner, it makes more sense in that context. Thanks! – JasonFruit Jun 12 '12 at 12:25

6 Answers6

78

Quite simple:

sorted(zipped, key=lambda x: x[1])
Ulrich Dangel
  • 4,515
  • 3
  • 22
  • 30
47
sorted(zipped, key = lambda t: t[1])
Owen
  • 38,836
  • 14
  • 95
  • 125
10
import operator
sorted(zipped, key=operator.itemgetter(1))

If you want it a little bit more faster, do ig = operator.itemgetter(1) and use ig as key function.

utdemir
  • 26,532
  • 10
  • 62
  • 81
5

In your case you don't need to sort at all because you just want an enumerated reversed list of your names:

>>> list(enumerate(names[::-1]))      # reverse by slicing
[(0, 'x'), (1, 't'), (2, 'a'), (3, 'd')]

>>> list(enumerate(reversed(names)))  # but reversed is also possible
[(0, 'x'), (1, 't'), (2, 'a'), (3, 'd')]

But if you need to sort it then you should use sorted (as provided by @utdemir or @Ulrich Dangel) because it will work on Python2 (zip and itertools.zip) and Python3 (zip) and won't fail with an AttributeError like .sort(...) (which only works on Python2 zip because there zip returns a list):

>>> # Fails with Python 3's zip:
>>> zipped = zip(names, vals)
>>> zipped.sort(lambda x: x[1])
AttributeError: 'zip' object has no attribute 'sort'

>>> # Fails with Python 2's itertools izip:
>>> from itertools import izip
>>> zipped = izip(names, vals)
>>> zipped.sort(lambda x: x[1])
AttributeError: 'itertools.izip' object has no attribute 'sort'

But sorted does work in each case:

>>> zipped = izip(names, vals)
>>> sorted(zipped, key=lambda x: x[1])
[('x', 0), ('t', 1), ('a', 2), ('d', 3)]

>>> zipped = zip(names, vals)  # python 3
>>> sorted(zipped, key=lambda x: x[1])
[('x', 0), ('t', 1), ('a', 2), ('d', 3)]
MSeifert
  • 145,886
  • 38
  • 333
  • 352
4

It's simpler and more efficient to zip them in order in the first place (if you can). Given your example it's pretty easy:

>>> names = 'datx'
>>> zip(reversed(names), xrange(len(names)))
<<< [('x', 0), ('t', 1), ('a', 2), ('d', 3)]
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108
0

Sort feature importance in a classifier (dtc=decision_tree):

for name, importance in sorted(zip(X_train.columns, 
                dtc.feature_importances_),key=lambda x: x[1]):
    print(name, importance)
Max Kleiner
  • 1,442
  • 1
  • 13
  • 14