7

Let's say I have a list of items I want to sort: items = [ item1, item2, item3 ]. The attribute I want to use to sort them is item.data.value, so I'd normally go:

sorted(items, key=attrgetter('data.value'))

And that'd work just fine. However, data can actually be None so obviously I couldn't access value.

How do you usually deal with scenarios like this?

PS: neither this question nor this one helped.

codeforester
  • 39,467
  • 16
  • 112
  • 140
dabadaba
  • 9,064
  • 21
  • 85
  • 155

6 Answers6

6
sorted(items, key=lambda i: i.data.value if i.data else 0)
Sergey Gornostaev
  • 7,596
  • 3
  • 27
  • 39
  • Yeah this is what I just tried. `None` instead of `0` but I think both comparisons would evaluate to the same things. – dabadaba Sep 19 '16 at 11:25
3

Use as key a tuple, like (False, value). If value is None, then the tuple should be (True, None).

Tuples are compared by their first element first, then the second, et cetera. False sorts before True. So all None values will be sorted to the end.

def none_to_end_key(item):
    value = item.data.value if item.data else None
    return (value is None, value)

sorted(items, key=none_to_end_key)

Will sort all None values to the end.

I see now that you have tagged your question Python-2.7, then this is probably overkill. In Python 3, comparing None to an integer or string raises an exception, so you can't simply sort a list with None and other values, and something like this is needed.

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
  • 1
    Not only is this 3-compatible, but unlike the currently accepted answer, it will put the Nones at the end even if item.data.value has both negative and positive values. You could even one-line it if you really wanted to (which I don't think is necessary, but some people seem to consider a killer feature for some reason.) – DSM Sep 19 '16 at 11:35
2

just filter for the None before sorting

sorted(filter(None, items), key=attrgetter('data.value'))
armak
  • 560
  • 5
  • 13
0

If you do not have a function handy that returns the key you want then just write your own.

def item_key(item):
    try:
        return item.data.value
    except AttributeError:
        return None

sorted(items, key=item_key)
Stop harming Monica
  • 12,141
  • 1
  • 36
  • 56
  • Could I lambda this? I actually just tried this: `sorted(items, key=lambda x: x.data.value if x.data else None)` Would this be similar to your answer? – dabadaba Sep 19 '16 at 11:24
  • @dabadaba It is not equivalent but it would satisfy the specification too (you did not specify what happens if `item` itself is `None` or if `item.data` is not `None` but it has no `value` attribute or how often `item.data.value` does not exist so I made arbitrary assumptions). I would use a named function in this case anyway. – Stop harming Monica Sep 19 '16 at 11:49
0

Just filter the None values first before sending them to sorted.

sorted(filter(None, items), key=attrgetter('data.value'))

And if you really want the None items too, you can do something like this:

# items already defined with values
new_items_list = sorted(filter(None, items), key=attrgetter('data.value'))
new_items_list.extend([None] * (len(items) - len(new_items_list)))

However, I am not sure whether you really need the None items.

EDIT: Fixed the end code which will retain the None items.

thiruvenkadam
  • 4,170
  • 4
  • 27
  • 26
-1

attrgetter with default value support.

behaves like dictionary.get()

D.get(k[,d]) -> D[k] if k in D, else d

d defaults to None. detailed info: groups.google.com:python-ideas

internety
  • 364
  • 3
  • 8