Python sort by multiple keys: tuples and reverse
Sort a Python list by multiple keys: see tuple priority, mixed ascending and descending order, stable ties and the difference between sorted and sort.
You want to sort words by length, then break ties without considering case. Adding reverse=True seems to fix the direction, until it also reverses the tie-breaker you wanted to keep ascending.
In short
To sort a Python list by multiple keys, pass sorted a key function that returns a tuple, from the primary criterion to the final tie-breaker. Python compares keys from left to right and preserves input order when keys are equal. reverse=True reverses the order of entire keys: for mixed directions, negate a numeric criterion or make several stable sorting passes, from the least important criterion to the primary one.
How does key set the priority of each criterion?
The first tuple item determines the order; the next item only breaks ties. Here, length takes priority over lowercase text.
words = ["pear", "Bee", "ant", "bee", "Ant", "fig"]for word in words: print(word, (len(word), word.lower()))result = sorted(words, key=lambda word: (len(word), word.lower()))print(result)print(words)$ python3 keys.pypear (4, 'pear')Bee (3, 'bee')ant (3, 'ant')bee (3, 'bee')Ant (3, 'ant')fig (3, 'fig')['ant', 'Ant', 'Bee', 'bee', 'fig', 'pear']['pear', 'Bee', 'ant', 'bee', 'Ant', 'fig']The word pear follows all words of length 3. Within that group, ant comes before bee, which comes before fig. The key function computes a ranking value for each item. It does not receive two words to compare.
| Comparison | Keys | Decision |
|---|---|---|
| pear / fig | (4, 'pear') / (3, 'fig') | Length decides: fig before pear |
| Bee / ant | (3, 'bee') / (3, 'ant') | Equal lengths: text decides |
| ant / Ant | (3, 'ant') / (3, 'ant') | Equal keys: keep input order |
The sort is stable. ant was before Ant, so it stays there; Bee stays before bee. Capitalization does not become a hidden extra criterion: Python does not fall back to comparing the original words to break these ties.
sorted returns a new list. The input keeps its order, as the last line shows. The same distinction between a new list and an update in place matters when you filter items during iteration.
Why does reverse=True also reverse the second key?
reverse=True requests descending order for the whole key. It does not target just the first tuple item.
We want descending lengths, then ascending words within each length. Compare the attempt using reverse with a key that negates only the length.
words = ["pear", "Bee", "ant", "bee", "Ant", "fig"]print(sorted(words, key=lambda word: (len(word), word.lower()), reverse=True))print(sorted(words, key=lambda word: (-len(word), word.lower())))$ python3 directions.py['pear', 'fig', 'Bee', 'bee', 'ant', 'Ant']['pear', 'ant', 'Ant', 'Bee', 'bee', 'fig']The first line puts fig before bee, then ant. The second keeps text ascending but puts longer words first: -4 comes before -3.
Negation works for the integer lengths in this example. It does not work for strings: there is no -word.lower(). Adding reverse=True to the negative-length key would reverse all criteria again.
How do you mix directions when a key is a string?
Sort by the secondary criterion first, then by the primary one. Because the second pass is stable, it preserves the order already established within groups that share a primary key.
This time, we want ascending lengths, then descending text.
words = ["pear", "Bee", "ant", "bee", "Ant", "fig"]secondary = sorted(words, key=str.lower, reverse=True)print(secondary)result = sorted(secondary, key=len)print(result)$ python3 passes.py['pear', 'fig', 'Bee', 'bee', 'ant', 'Ant']['fig', 'Bee', 'bee', 'ant', 'Ant', 'pear']After the first pass, text is in descending order. The second groups words by length without changing the order of words of length 3.
| Requested order | Construction |
|---|---|
| Ascending length, ascending text | One key: (len(word), word.lower()) |
| Descending length, ascending text | One key: (-len(word), word.lower()) |
| Ascending length, descending text | Descending text first, ascending length second |
With three criteria, the same rule applies: sort by the final tie-breaker first and the primary criterion last. Writing the priority in plain language before coding helps keep the passes in the right order.
What is the difference between sorted and list.sort?
sorted builds a list, while list.sort changes the existing list and returns None. Both support the same key and direction choices, and both guarantee stability.
words = ["pear", "Bee", "ant"]alias = wordsresult = words.sort(key=lambda word: (len(word), word.lower()))print(result)print(words)print(alias is words)print(sorted([], key=lambda word: (len(word), word.lower())))$ python3 inplace.pyNone['ant', 'Bee', 'pear']True[]Writing words = words.sort(...) therefore replaces your reference with None. Use sorted when the caller needs to reuse the input; use sort when the contract calls for changing that list.
sort_ranked_words
Sort words by length, lowercase text, then the vowel count specified in the statement. Return a new list and preserve the original order when all keys are equal. The challenge adds a third tie-breaker for you to build.
Frequently asked questions
Should I use lower() or lower in the key?
For a single key, key=str.lower passes the function to the sorter. In a composite key, lambda word: (len(word), word.lower()) computes both values for the word it receives.
Does reverse=True break stability?
No. Items with equal keys keep their input order even with reverse=True. Reversing the finished list with a [::-1] slice would reverse ties too.
Does lower produce French alphabetical order?
No. It transforms case, then strings are compared in Unicode order. Language-specific ordering of accents requires a separate collation policy. These examples use ASCII words.
Can keys mix None and numbers?
A mixture of values that cannot be compared can raise TypeError. Decide where missing values belong, then build a key that separates them before comparing values; the sorter cannot infer that contract.
Sources
