0

I have a list of strings I would like to sort. However, some strings are one word, others are two words. Also, I would like to first sort by the second word, then the first.

I'm relatively new to the idea of lambda functions and also how a key works for the sort() method, so I've tried the following (which doesn't work):

list_to_sort = ['1 month', '1 year', '3 months', '3 years', '6 months', 'daily']

def sort_func(x):
    x.split()
    if len(x) == 1:
        return x[0]
    else:
        return x[1]

cols.sort(key=sort_func)

# I would like the output to be: 
# ['daily', '1 month', '3 months', '6 months', '1 year', '3 years']

Instead, the list doesn't change at all. I'm not too certain how the "key=" works in the sort() method though.

Devesh Kumar Singh
  • 20,259
  • 5
  • 21
  • 40
Jr Analyst
  • 58
  • 6
  • Possible duplicate of [Sort a list by multiple attributes?](https://stackoverflow.com/questions/4233476/sort-a-list-by-multiple-attributes) – Sebastien D Jun 18 '19 at 14:48
  • What are you sorting by (length, alphabetical order, etc)? – Agi Hammerthief Jun 18 '19 at 14:49
  • Your required output requires more logic than your current code (even if worked "as expected") has. How would it know that `'years'` has more significance than `'months'`? – DeepSpace Jun 18 '19 at 14:52
  • @DeepSpace Alphabetically, daily>month>months>year>years. So sorting by the last word would get be the order I want, no? – Jr Analyst Jun 18 '19 at 14:56
  • @JrAnalyst If you want pure alphabetical order then yes. If you want a more logical one then no. For example, if you have `['2 years', '999 days']` then after sorting you will have `['999 days', '2 years']` (which is correct alphabetically but wrong mathematically). – DeepSpace Jun 18 '19 at 15:02

3 Answers3

2

You were almost there, x.split() returns a new list, so you would need to assign the return value to a variable. Also since one of your word after splitting is a float, you need to take care of it when creating the key

list_to_sort = ['', '1 month', '1 year', '3 months', '3 years', '6 months', 'daily', '11 months', '12 years']

def sort_func(x):

    #Assign result of split to li
    li = x.split()

    #If 1 element, return word, 0
    if len(li) == 1:
        return li[-1], 0
    #If 2 elements, return word and number cast to int
    elif len(li) == 2:
        return li[-1], int(li[0])

    #If no elements, return empty string and 0
    else:
        return '',0

list_to_sort.sort(key=sort_func)
print(list_to_sort)

The output will be

['', 'daily', '1 month', '3 months', '6 months', '11 months', '1 year', '3 years', '12 years']
Devesh Kumar Singh
  • 20,259
  • 5
  • 21
  • 40
1

.split is not in place. x.split() creates a list and throws it away.

Since you are essentially sorting by the last word (if you have 1 word then the "last word" is that 1 word), a better implementation for sort_func would be

return x.split()[-1]

or as a lambda:

cols.sort(key=lambda x: x.split()[-1])
DeepSpace
  • 78,697
  • 11
  • 109
  • 154
1

Here is another solution:

list_to_sort = ['1 month', '1 year', '3 months', '3 years', '6 months', 'daily']

list_to_sort = sorted(list_to_sort, key=lambda x: tuple(reversed(x.split())))
print(list_to_sort)

# >> ['daily', '1 month', '3 months', '6 months', '1 year', '3 years']

NB: This should work wathever the number of words is, including empty strings.

Example:

['', 'daily', '2 months 1 day', '3 months', '6 months', '1 year', '3 years']

Update: Keep in mind that the numbers in your strings will be sorted by as characters, not as integers:

['1', '11', '2', ...]

If you want to avoid this, the line above should be changed to:

list_to_sort = sorted(list_to_sort, key=lambda x: tuple(reversed([float(item) if item.isnumeric() else item for item in x.split()])))
olinox14
  • 6,177
  • 2
  • 22
  • 39