2

Let's say I have such a list:

['word_4_0_w_7',
 'word_4_0_w_6',
 'word_3_0_w_10',
 'word_3_0_w_2']

and I want to sort them according to number that comes after "word" and according to number after "w". It will look like this:

   ['word_3_0_w_2',
     'word_3_0_w_10',
     'word_4_0_w_6',
     'word_4_0_w_7']

What comes in mind is to create a bunch of list and according to index after "word" stuff them with sorted strings according "w", and then merge them.

Is in Python more clever way to do it?

Mangu Singh Rajpurohit
  • 10,806
  • 4
  • 68
  • 97
Il'ya Zhenin
  • 1,272
  • 2
  • 20
  • 31

3 Answers3

3

Use Python's key functionality, in conjunction with other answers:

def mykey(value):
    ls = value.split("_")
    return int(ls[1]), int(ls[-1])

newlist = sorted(firstlist, key=mykey)
## or, if you want it in place:
firstlist.sort(key=mykey)

Python will be more efficient with key vs cmp.

dwanderson
  • 2,775
  • 2
  • 25
  • 40
1

You can use a function to extract the relevant parts of your string and then use those parts to sort:

a = ['word_4_0_w_7', 'word_4_0_w_6', 'word_3_0_w_10', 'word_3_0_w_2']

def sort_func(x):
    parts = x.split('_');
    sort_key = parts[1]+parts[2]+"%02d"%int(parts[4])
    return sort_key

a_sorted = sorted(a,key=sort_func)

The expression "%02d" %int(x.split('_')[4]) is used to add a leading zero in front of second number otherwise 10 will sort before 2. You may have to do the same with the number extracted by x.split('_')[2].

mguijarr
  • 7,641
  • 6
  • 45
  • 72
Alex
  • 21,273
  • 10
  • 61
  • 73
  • While lambda's are great for one-offs, and this does seem like a pretty simple one, I'd say the expression is just complex enough that an inline lambda like that is unreadable/unmaintainable. Way too easy to make a formatting mistake, and the next person who comes along and looks at the code will have to spend a few minutes teasing out what it means and if it's correct – dwanderson Nov 13 '15 at 13:44
  • I have refactored the code so the lambda is defined on a separate line. You can now call it to test if it works. – Alex Nov 13 '15 at 14:58
  • Why not just make it a two or three line function then? My whole point was that it's hard to read/maintain. Assigning the lambda to a function, instead of using a function proper, seems even less readable, since it would probably cause every Python programmer to pause and ask "Now why did they bother to do assign the lambda instead of just making a normal function?" – dwanderson Nov 13 '15 at 15:46
  • I changed as Guido van Rossum doesn't seem to like lambda operators either, see http://stackoverflow.com/questions/134626/which-is-more-preferable-to-use-in-python-lambda-functions-or-nested-functions – Alex Nov 13 '15 at 15:56
1

You can provide a function to the sort() method of list objects:

l = ['word_4_0_w_7',
     'word_4_0_w_6',
     'word_3_0_w_10',
     'word_3_0_w_2']

def my_key_func(x):
    xx = x.split("_")
    return (int(xx[1]), int(xx[-1]))
l.sort(key=my_key_func)

Output:

print l
['word_3_0_w_2', 'word_3_0_w_10', 'word_4_0_w_6', 'word_4_0_w_7']

edit: Changed code according to comment by @dwanderson ; more info on this can be found here.

mguijarr
  • 7,641
  • 6
  • 45
  • 72
  • I think it's recommended to use the `key` functionality, rather than the `cmp`, since sorting will calculate the `key` O(n) times but can do O(n**2) `cmp`s (I think). Simple change would just be: `def key(x): xx = x.split("_"); return int(xx[1]), int(xx[-1])` and then `l.sort(key=mykey)`. The output will be the same, but it's more efficient, and the preferred Python way. – dwanderson Nov 13 '15 at 13:37