4

Lets assume I've got a following python list(this is only example):

my_list = [{'user': 'Joe', 'score': 14},
           {'user': 'Foo', 'score': 12},
           {'user': 'May', 'score': 12},
           {'user': 'Kat', 'score': 12},
           {'user': 'Doe', 'score': 13}]

I need to sort this list in ascending order by score and descending order by a username. Expected sort result:

my_list = [{'user': 'May', 'score': 12},
           {'user': 'Kat', 'score': 12},
           {'user': 'Foo', 'score': 12},
           {'user': 'Doe', 'score': 13},
           {'user': 'Joe', 'score': 14}]

So, I could do something like this if I want everything to be in ascending order:

my_list.sort(key=lambda x: (x['score'], x['user']))

For integers it is easy to solve this problem just adding - in front of it:

my_list.sort(key=lambda x: (-x['score'], x['user']))

Unfortunately, strings can not be negative :-|

I need a generic solution that doesn't involve 'reverse=True'. Lambda function is dynamically generated based on a user config.

Thoughts?

Vlad Benya
  • 41
  • 3

3 Answers3

4

Your current solution will work fine if you set the reverse parameter of list.sort to True:

>>> my_list = [{'user': 'Joe', 'score': 14},
...            {'user': 'Foo', 'score': 12},
...            {'user': 'May', 'score': 12},
...            {'user': 'Kat', 'score': 12},
...            {'user': 'Doe', 'score': 13}]
>>> my_list.sort(key=lambda x: (-x['score'], x['user']), reverse=True)
>>> pprint(my_list) # pprint makes the nice output
[{'score': 12, 'user': 'May'},
 {'score': 12, 'user': 'Kat'},
 {'score': 12, 'user': 'Foo'},
 {'score': 13, 'user': 'Doe'},
 {'score': 14, 'user': 'Joe'}]
>>>

This will sort the list in reverse order.


Edit:

Since the names and scores have two different sort orders, you will need to use two separate sorts to achieve your desired output:

>>> my_list = [{'user': 'Joe', 'score': 14},
...            {'user': 'Foo', 'score': 12},
...            {'user': 'May', 'score': 12},
...            {'user': 'Kat', 'score': 12},
...            {'user': 'Doe', 'score': 13}]
>>> my_list.sort(key=lambda x: x['user'], reverse=True)
>>> my_list.sort(key=lambda x: x['score'])
>>> pprint(my_list)
[{'score': 12, 'user': 'May'},
 {'score': 12, 'user': 'Kat'},
 {'score': 12, 'user': 'Foo'},
 {'score': 13, 'user': 'Doe'},
 {'score': 14, 'user': 'Joe'}]
>>>
  • It works only for this particular case just because you can play with the sign of the integer values. reverse=True affects a whole list order that is not acceptable. I need something that can affect an order of a specific column. Like in SQL: ORDER by score ASC, USER desc, last_name ASC. – Vlad Benya Jan 07 '15 at 23:16
  • See my edit. You'll need to use two sorts in that case (one for descending, the other for ascending). –  Jan 07 '15 at 23:26
2

The ord of a string can be negative:

my_list.sort(key=lambda x: (x["score"],[-ord(x) for x in x["user"]])))


In [50]:  my_list.sort(key=lambda x: (x["score"],[-ord(x) for x in x["user"]]))

In [51]: my_list
Out[51]: 
[{'score': 12, 'user': 'May'},
 {'score': 12, 'user': 'Kat'},
 {'score': 12, 'user': 'Foo'},
 {'score': 13, 'user': 'Doe'},
 {'score': 14, 'user': 'Joe'}]
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
1

you need to add the reverse=True keyword for descending order.

>>> my_list.sort(key=lambda x: (-x['score'], x['user']),reverse=True)
>>> my_list
[{'score': 12, 'user': 'May'}, {'score': 12, 'user': 'Kat'}, {'score': 12, 'user': 'Foo'}, {'score': 13, 'user': 'Doe'}, {'score': 14, 'user': 'Joe'}]
Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • This just a custom solution just for this example. I may have three or four fields that I need to sort on. Each of them may be descending as well as others ascending. All fields can be strings. I am looking for a generic solution. – Vlad Benya Jan 07 '15 at 23:06
  • @VladBenya what you mean ? always when you use `reverse=True` `sort` function sort the iterable reverse ! – Mazdak Jan 07 '15 at 23:08
  • @VladBenya so for that aim you need to update your list , and couldn't just use `sort` – Mazdak Jan 07 '15 at 23:10
  • Just imagine that score is a user last name that is a string. I want to sort this list in ascending order by user name and descending order their last names together. reverse=True will not help here since it affect a final order of a whole list. – Vlad Benya Jan 07 '15 at 23:12