Sorting and Ranking
Sort by Multiple Keys
Build (cat, score, name) tuples from each record and sort them with Python's
tuple comparison, which compares left-to-right. With pandas, passing a list of
column names to sort_values sorts hierarchically — primary key first,
secondary key as a tiebreaker.
By hand
Build pairs as a list of (cat, score, name) tuples — one per row. Call
pairs.sort(), which compares tuples left-to-right: first by cat, then by
score within the same category. Extract names from the sorted pairs.
naive.py
Replay: real traced execution (multi-file project)
# trace: ignore records
records = [
{'name': 'al', 'cat': 'B', 'score': 80},
{'name': 'bo', 'cat': 'A', 'score': 55},
{'name': 'cy', 'cat': 'B', 'score': 70},
{'name': 'di', 'cat': 'A', 'score': 90},
]
pairs = []
for r in records:
pairs.append((r['cat'], r['score'], r['name']))
pairs.sort()
result = [p[2] for p in pairs]
print('RESULT:', result)
{'name': 'al', 'cat': 'B', 'score': 80},
2records = [3 {'name': 'al', 'cat': 'B', 'score': 80},4 {'name': 'bo', 'cat': 'A', 'score': 55},{'name': 'bo', 'cat': 'A', 'score': 55},
3{'name': 'al', 'cat': 'B', 'score': 80},4{'name': 'bo', 'cat': 'A', 'score': 55},5{'name': 'cy', 'cat': 'B', 'score': 70},{'name': 'cy', 'cat': 'B', 'score': 70},
4{'name': 'bo', 'cat': 'A', 'score': 55},5{'name': 'cy', 'cat': 'B', 'score': 70},6{'name': 'di', 'cat': 'A', 'score': 90},{'name': 'di', 'cat': 'A', 'score': 90},
5 {'name': 'cy', 'cat': 'B', 'score': 70},6 {'name': 'di', 'cat': 'A', 'score': 90},7]records = [
1# trace: ignore records2records = [3 {'name': 'al', 'cat': 'B', 'score': 80},pairs ← []
7]8pairs = []9for r in records:values this step[]pairsr ← {'name': 'al', 'cat': 'B', 'score': 80}
8pairs = []9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))values this step{'name': 'al', 'cat': 'B', 'score': 80}rpairs ← [('B', 80, 'al')]
9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))11pairs.sort()values this step[] → [('B', 80, 'al')]pairsr ← {'name': 'bo', 'cat': 'A', 'score': 55}
8pairs = []9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))values this step{'name': 'al', 'cat': 'B', 'score': 80} → {'name': 'bo', 'cat': 'A', 'score': 55}rpairs ← [('B', 80, 'al'), ('A', 55, 'bo')]
9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))11pairs.sort()values this step[('B', 80, 'al')] → [('B', 80, 'al'), ('A', 55, 'bo')]pairsr ← {'name': 'cy', 'cat': 'B', 'score': 70}
8pairs = []9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))values this step{'name': 'bo', 'cat': 'A', 'score': 55} → {'name': 'cy', 'cat': 'B', 'score': 70}rpairs ← [('B', 80, 'al'), ('A', 55, 'bo'), ('B', 70, 'cy')]
9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))11pairs.sort()values this step[('B', 80, 'al'), ('A', 55, 'bo')] → [('B', 80, 'al'), ('A', 55, 'bo'), ('B', 70, 'cy')]pairsr ← {'name': 'di', 'cat': 'A', 'score': 90}
8pairs = []9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))values this step{'name': 'cy', 'cat': 'B', 'score': 70} → {'name': 'di', 'cat': 'A', 'score': 90}rpairs ← [('B', 80, 'al'), ('A', 55, 'bo'), ('B', 70, 'cy'), ('A', 90, 'di')]
9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))11pairs.sort()values this step[('B', 80, 'al'), ('A', 55, 'bo'), ('B', 70, 'cy')] → [('B', 80, 'al'), ('A', 55, 'bo'), ('B', 70, 'cy'), ('A', 90, 'di')]pairsfor r in records:
8pairs = []9for r in records:10 pairs.append((r['cat'], r['score'], r['name']))pairs ← [('A', 55, 'bo'), ('A', 90, 'di'), ('B', 70, 'cy'), ('B', 80, 'al')]
10 pairs.append((r['cat'], r['score'], r['name']))11pairs.sort()12result = [p[2] for p in pairs]values this step[('B', 80, 'al'), ('A', 55, 'bo'), ('B', 70, 'cy'), ('A', 90, 'di')] → [('A', 55, 'bo'), ('A', 90, 'di'), ('B', 70, 'cy'), ('B', 80, 'al')]pairsresult ← ['bo', 'di', 'cy', 'al']
11pairs.sort()12result = [p[2] for p in pairs]13print('RESULT:', result)values this step['bo', 'di', 'cy', 'al']resultstdout ← RESULT: ['bo', 'di', 'cy', 'al']
12result = [p[2] for p in pairs]13print('RESULT:', result)values this stepRESULT: ['bo', 'di', 'cy', 'al']stdout
With pandas
df.sort_values(['cat', 'score']) sorts by cat first, using score to
break ties within each category. The snapshot shows all three columns in their
final sorted order.
library.py
import pandas as pd
from dalib.display import set_display
set_display()
records = [
{'name': 'al', 'cat': 'B', 'score': 80},
{'name': 'bo', 'cat': 'A', 'score': 55},
{'name': 'cy', 'cat': 'B', 'score': 70},
{'name': 'di', 'cat': 'A', 'score': 90},
]
df = pd.DataFrame(records)
sorted_df = df.sort_values(['cat', 'score'])
print('cats:', sorted_df['cat'].tolist())
print('scores:', sorted_df['score'].tolist())
print('names:', sorted_df['name'].tolist())
print('RESULT:', sorted_df['name'].tolist())
cats: ['A', 'A', 'B', 'B']
scores: [55, 90, 70, 80]
names: ['bo', 'di', 'cy', 'al']
RESULT: ['bo', 'di', 'cy', 'al']
Implementation notes
- The list of column names is sorted hierarchically left-to-right: first column is primary, second is the tiebreaker, and so on. This mirrors how Python tuple comparison works — the naive half uses the same rule explicitly.
- Control direction per column with a matching list:
df.sort_values(['cat', 'score'], ascending=[True, False])sorts categories ascending but scores descending within each category. sort_valuesdefaults tokind='quicksort', which is not guaranteed stable. Passkind='stable'(orkind='mergesort') to preserve the original relative order of rows with equal keys, matching Python'slist.sort()guarantee.- Cross-reference:
sort-two-keys(python-data-basics) for the pure-Python tuple-sort version.