Murphy4 - 3 months ago 28

Python Question

Consider the following dictionary:

`data = {'A':{'total':3},`

'B':{'total':5},

'C':{'total':0},

'D':{'total':0},

}

The desired order for above is B, A, C, D. Order by descending total, then by ascending key.

When I call

`sorted(data, key=lambda x: (data[x]['total'], x), reverse=True)`

I get B,A,D,C because reverse is called on both keys.

Is there an efficient way to solve this?

Answer

Sort on *negative* total, that'll reverse put the totals in reverse order without having to use `reverse=True`

. Ties are then broken on the key in forward order:

```
sorted(data, key=lambda x: (-data[x]['total'], x))
```

Demo:

```
>>> data = {'A':{'total':3},
... 'B':{'total':5},
... 'C':{'total':0},
... 'D':{'total':0},
... }
>>> sorted(data, key=lambda x: (-data[x]['total'], x))
['B', 'A', 'C', 'D']
```

This trick only works for numeric components in a sort key; if you have multiple keys that require a sort direction change that are *not* numeric, you'd have to do a multi-pass sort (sort multiple times, from last key to first):

```
# when you can't take advantage of numerical values to reverse on
# you need to sort repeatedly from last key to first.
# Here, sort forward by dict key, then in reverse by total
bykey = sorted(data)
final = sorted(bykey, key=lambda x: data[x]['total'], reverse=True)
```

This works because the Python sort algorithm is *stable*; two elements keep their relative positions if the current sort key result is equal for those two elements.