Sort items by a primary key (category) and break ties with a secondary key (value). The replay shows remain shrinking as each minimum (cat, val) pair is selected, with result_cats and result_vals growing in tandem.

By hand

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.

naive.py
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)))
library.py
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; min discards it after choosing the winner, so only the integer best appears as a traced variable.
  • result_cats and result_vals are parallel output lists that mirror the input structure; list(zip(...)) reassembles them into (cat, val) pairs for the RESULT line.