-1

For example I have a dictionary:

a = {'B': 2674, 'C': 2000, 'A': 2674}

and I need to sort it by the values in descending order. BUT, when the values are equal, then by key, alphabetically, ascending. Outputs a list with the keys.

b = sorted(a, key=lambda d: (d[1], d[0]))

Is there somehow a way to set the reverse separately?

searchfind
  • 47
  • 1
  • 5
  • 1
    I assume it should be `a = {'B': 2674, 'C': 2000, 'A': 2674}` but it's up to the asker to verify that. – Matthias Oct 19 '20 at 16:16

2 Answers2

2

With the numeric values, you can use the key function to achieve both sorting criteria by simply negating the values. Then use a comprehension to extract only the keys:

b = [k for k, _ in sorted(a.items(), key=lambda i: (-i[1], i[0]))]
# ['A', 'B', 'C']
user2390182
  • 72,016
  • 6
  • 67
  • 89
1

EDIT:

This answer is wrong. see @ShadowRanger's comments.


From the docs

s = sorted(a, key=lambda d: d[1]) # sort on secondary key
sorted(s, key=lambda d: d[0], reverse=True) # now sort on primary key, descending

This is the way it was meant to be done

The Timsort algorithm used in Python does multiple sorts efficiently because it can take advantage of any ordering already present in a dataset.

Gulzar
  • 23,452
  • 27
  • 113
  • 201
  • Note: The quote about "TimSort does multiple sorts efficiently because it can take advantage of any ordering already present in a dataset" is really about resorting a `list` by the *same* key after `append`ing more items (so it's mostly sorted, plus some unsorted stuff at the end). It doesn't apply here; the ordering resulting from sorting on the secondary key will (usually) destroy any preexisting order on the primary key, so both sorts will be operating on "unsorted" data from the perspective of the key they care about, causing them both to do `n log n` work, no "mostly `n` work" shortcuts. – ShadowRanger Oct 19 '20 at 16:50
  • @ShadowRanger That is interesting. How would you understand the docs then? – Gulzar Oct 20 '20 at 14:26
  • 1
    The docs are talking about the case of `mylist = [...lots of stuff...]`, `mylist.sort()`, `mylist.append(...)` a few times, followed by another `mylist.sort()`. Where a simple quicksort or mergesort wouldn't benefit from the "lots of stuff" part of the list already being sorted (so it would still be `n log n` work in most cases), TimSort is (*very* roughly) only doing new `m log m` work (where `m` is the number of newly appended items), and keeping close to `n` work for the existing sorted data (almost exactly if the new data sorts entirely before or after the existing data). – ShadowRanger Oct 20 '20 at 14:30
  • 1
    In another language, if the data is huge, and you're adding just a few items, you'd want to keep the new data separate, sort it independently, and then merge the two sorted lists, keeping the work down to `O(m log m)` to sort the new data, plus a `O(n + m)` merge cost, to avoid paying a full `O((n+m) log (n+m))` sort cost every time you add new data. Python made TimSort handle that for you so the "obvious solution" of adding new data and resorting just worked (it does offer `heapq.merge` for the case of merging multiple sorted inputs lazily, but it's rarely needed). – ShadowRanger Oct 20 '20 at 14:33