Sergey
Sergey

Reputation: 12417

How do I sort a list with "Nones last"

I'm sorting a list of dicts by a key:

groups = sorted(groups, key=lambda a: a['name'])

some of the dicts have name set to None, and Python 2 places None values before any other, so they're placed at the front of the sorted list. A naive fix would be

groups = sorted(groups, key=lambda a: a['name'] or 'zzzz')

but, obviously, that would not work for any non-Latin names.

What is a nice and Pythonic way of sorting a list containing None values so that those values are placed at the end of the list?

Upvotes: 5

Views: 683

Answers (2)

Lambda Fairy
Lambda Fairy

Reputation: 14686

You can create your own "infinity" object:

from functools import total_ordering

@total_ordering
class Infinity:
    def __eq__(self, other):
        return type(other) == Infinity
    def __lt__(self, other):
        return False

Use it like so:

>>> lis = [{'name': 1}, {'name': None}, {'name': 0}, {'name': 2}]
>>> sorted(lis, key=lambda a: Infinity() if a['name'] is None else a['name'])
[{'name': 0}, {'name': 1}, {'name': 2}, {'name': None}]

Upvotes: 2

David Robinson
David Robinson

Reputation: 78610

You can do this by keying it on a tuple:

groups = sorted(groups, key=lambda a: (a['name'] is None, a['name']))

This works because Python compares tuples lexicographically (on the first element, then on the second to break ties), and because False gets sorted earlier than True. A list of groups like

[{'name': 0}, {'name': 1}, {'name': 2}, {'name': 3}, {'name': 4}, {'name': None}]

will become

[(False, 0), (False, 1), (False, 2), (False, 3), (False, 4), (True, None)]

The tuple that starts with True will necessarily end up at the end, and the rest, since the first value ties, will be sorted by the second value.

Upvotes: 14

Related Questions