Joining Records
Lookup Enrich
Attach a category label to each product by looking up its code in a side
dict. Unknown codes fall back to a default value. The trace shows cat
resolving at each step and result accumulating (name, category) pairs,
with ('mat', 'other') appearing for the unrecognised code 'z'.
By hand
Iterate over products and codes together. For each pair call
categories.get(code, 'other') to resolve the label, then append the
(name, cat) tuple to result.
naive.py
Replay: real traced execution (multi-file project)
products = ['pen', 'cup', 'bag', 'mat']
codes = ['a', 'b', 'b', 'z']
categories = {'a': 'work', 'b': 'home'}
result = []
for name, code in zip(products, codes):
cat = categories.get(code, 'other')
result.append((name, cat))
print('RESULT:', result)
products ← ['pen', 'cup', 'bag', 'mat']
1products = ['pen', 'cup', 'bag', 'mat']2codes = ['a', 'b', 'b', 'z']values this step['pen', 'cup', 'bag', 'mat']productscodes ← ['a', 'b', 'b', 'z']
1products = ['pen', 'cup', 'bag', 'mat']2codes = ['a', 'b', 'b', 'z']3categories = {'a': 'work', 'b': 'home'}values this step['a', 'b', 'b', 'z']codescategories ← {'a': 'work', 'b': 'home'}
2codes = ['a', 'b', 'b', 'z']3categories = {'a': 'work', 'b': 'home'}4result = []values this step{'a': 'work', 'b': 'home'}categoriesresult ← []
3categories = {'a': 'work', 'b': 'home'}4result = []5for name, code in zip(products, codes):values this step[]resultcode ← 'a', name ← 'pen'
4result = []5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')values this step'a'code'pen'namecat ← 'work'
5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')7 result.append((name, cat))values this step'work'catresult ← [('pen', 'work')]
6 cat = categories.get(code, 'other')7 result.append((name, cat))8print('RESULT:', result)values this step[] → [('pen', 'work')]resultcode ← 'b', name ← 'cup'
4result = []5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')values this step'a' → 'b'code'pen' → 'cup'namecat ← 'home'
5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')7 result.append((name, cat))values this step'work' → 'home'catresult ← [('pen', 'work'), ('cup', 'home')]
6 cat = categories.get(code, 'other')7 result.append((name, cat))8print('RESULT:', result)values this step[('pen', 'work')] → [('pen', 'work'), ('cup', 'home')]resultname ← 'bag'
4result = []5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')values this step'cup' → 'bag'namecat = categories.get(code, 'other')
5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')7 result.append((name, cat))result ← [('pen', 'work'), ('cup', 'home'), ('bag', 'home')]
6 cat = categories.get(code, 'other')7 result.append((name, cat))8print('RESULT:', result)values this step[('pen', 'work'), ('cup', 'home')] → [('pen', 'work'), ('cup', 'home'), ('bag', 'home')]resultcode ← 'z', name ← 'mat'
4result = []5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')values this step'b' → 'z'code'bag' → 'mat'namecat ← 'other'
5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')7 result.append((name, cat))values this step'home' → 'other'catresult ← [('pen', 'work'), ('cup', 'home'), ('bag', 'home'), ('mat', 'other')]
6 cat = categories.get(code, 'other')7 result.append((name, cat))8print('RESULT:', result)values this step[('pen', 'work'), ('cup', 'home'), ('bag', 'home')] → [('pen', 'work'), ('cup', 'home'), ('bag', 'home'), ('mat', 'other')]resultfor name, code in zip(products, codes):
4result = []5for name, code in zip(products, codes):6 cat = categories.get(code, 'other')stdout ← RESULT: [('pen', 'work'), ('cup', 'home'), ('bag', 'home'), ('mat', 'other')]
7 result.append((name, cat))8print('RESULT:', result)values this stepRESULT: [('pen', 'work'), ('cup', 'home'), ('bag', 'home'), ('mat', 'other')]stdout
The Pythonic way
A list comprehension folds the lookup and the pairing into one expression.
The get call with its default handles unknown codes inline.
library.py
products = ['pen', 'cup', 'bag', 'mat']
codes = ['a', 'b', 'b', 'z']
categories = {'a': 'work', 'b': 'home'}
result = [(name, categories.get(code, 'other'))
for name, code in zip(products, codes)]
print('RESULT:', result)
RESULT: [('pen', 'work'), ('cup', 'home'), ('bag', 'home'), ('mat', 'other')]
Implementation notes
- This is the everyday "attach a label" pattern — a special case of a left join where the right table is a flat key→value dict rather than a record table. Row count is always preserved because every code produces a value (matched or default).
- The equivalent pandas operation is
df['cat'] = df['code'].map(categories)or.map(categories).fillna('other')— see thepython-pandastrack lessonmap-lookup-columnfor the API form. - Unlike
left-join-with-missing, the fallback is a real label ('other') rather thanNone, so downstream code does not need aNone-guard.