Sorting and Ranking
Sort Two Keys
Sort items by a primary key (category) and break ties with a secondary key
(value). The trace shows remain shrinking as each minimum (cat, val) pair
is selected, with result_cats and result_vals growing in tandem.
By hand
Reuse the selection-of-minimum pattern from sort-by-key, but the key
function now returns a pair (cats[i], vals[i]). Python compares tuples
lexicographically: category first, then value for ties. All 'a' items are
therefore picked before any 'b' item, and within each category the lower
value wins.
cats = ['b', 'a', 'b', 'a', 'b', 'a']
vals = [3, 2, 1, 3, 2, 1]
remain = list(range(len(cats)))
result_cats = []
result_vals = []
while remain:
best = min(remain, key=lambda i: (cats[i], vals[i]))
result_cats.append(cats[best])
result_vals.append(vals[best])
remain.remove(best)
print('RESULT:', list(zip(result_cats, result_vals)))
cats ← ['b', 'a', 'b', 'a', 'b', 'a']
1cats = ['b', 'a', 'b', 'a', 'b', 'a']2vals = [3, 2, 1, 3, 2, 1]values this step['b', 'a', 'b', 'a', 'b', 'a']catsvals ← [3, 2, 1, 3, 2, 1]
1cats = ['b', 'a', 'b', 'a', 'b', 'a']2vals = [3, 2, 1, 3, 2, 1]3remain = list(range(len(cats)))values this step[3, 2, 1, 3, 2, 1]valsremain ← [0, 1, 2, 3, 4, 5]
2vals = [3, 2, 1, 3, 2, 1]3remain = list(range(len(cats)))4result_cats = []values this step[0, 1, 2, 3, 4, 5]remainresult_cats ← []
3remain = list(range(len(cats)))4result_cats = []5result_vals = []values this step[]result_catsresult_vals ← []
4result_cats = []5result_vals = []6while remain:values this step[]result_valsbest ← 5, result_cats ← ['a'], result_vals ← [1], remain ← [0, 1, 2, 3, 4]
pass 1 of 65result_vals = []6while remain:7 best = min(remain, key=lambda i: (cats[i], vals[i]))8 result_cats.append(cats[best])9 result_vals.append(vals[best])10 remain.remove(best)11print('RESULT:', list(zip(result_cats, result_vals)))values this step5best[] → ['a']result_cats[] → [1]result_vals[0, 1, 2, 3, 4, 5] → [0, 1, 2, 3, 4]remainAll 6 passes — pass 1 is the card above pass bestresult_catsresult_valsremain1 5 [] → ['a'] [] → [1] [0, 1, 2, 3, 4, 5] → [0, 1, 2, 3, 4] 2 5 → 1 ['a'] → ['a', 'a'] [1] → [1, 2] [0, 1, 2, 3, 4] → [0, 2, 3, 4] 3 1 → 3 ['a', 'a'] → ['a', 'a', 'a'] [1, 2] → [1, 2, 3] [0, 2, 3, 4] → [0, 2, 4] 4 3 → 2 ['a', 'a', 'a'] → ['a', 'a', 'a', 'b'] [1, 2, 3] → [1, 2, 3, 1] [0, 2, 4] → [0, 4] 5 2 → 4 ['a', 'a', 'a', 'b'] → ['a', 'a', 'a', 'b', 'b'] [1, 2, 3, 1] → [1, 2, 3, 1, 2] [0, 4] → [0] 6 4 → 0 ['a', 'a', 'a', 'b', 'b'] → ['a', 'a', 'a', 'b', 'b', 'b'] [1, 2, 3, 1, 2] → [1, 2, 3, 1, 2, 3] [0] → [] while remain:
5result_vals = []6while remain:7 best = min(remain, key=lambda i: (cats[i], vals[i]))stdout ← RESULT: [('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3)]
10 remain.remove(best)11print('RESULT:', list(zip(result_cats, result_vals)))values this stepRESULT: [('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3)]stdout
The Pythonic way
zip(cats, vals) forms (cat, val) pairs; sorted(..., key=lambda pair: (pair[0], pair[1])) orders them by the same two-element tuple key. Because
Python already compares tuples lexicographically, the key here is explicit
but could be omitted.
cats = ['b', 'a', 'b', 'a', 'b', 'a']
vals = [3, 2, 1, 3, 2, 1]
result = sorted(zip(cats, vals), key=lambda pair: (pair[0], pair[1]))
print('RESULT:', result)
RESULT: [('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3)]
Implementation notes
- Two categories × three values keeps the data small enough that all reprs stay well within the 80-character limit.
- The key lambda
lambda i: (cats[i], vals[i])constructs a fresh tuple for every comparison;mindiscards it after choosing the winner, so only the integerbestappears as a traced variable. result_catsandresult_valsare parallel output lists that mirror the input structure;list(zip(...))reassembles them into (cat, val) pairs for the RESULT line.