Rename Keys
A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- medium
- Seniority
- L3
Problem
Return a copy of record with every field listed in mapping renamed from its incoming name to its canonical one, so user records from different upstream systems line up, while fields the mapping does not list carry through unchanged. An entry in mapping for a field this record lacks has no effect.
Old names out. New names in.
Example 1
Input:
record = {"age":30,"eml":"alice@co.com","usr_nm":"alice"}, mapping = {"eml":"email","usr_nm":"username"}Output:
{"age":30,"email":"alice@co.com","username":"alice"}Example 2
Input:
record = {"a":1,"b":2}, mapping = {}Output:
{"a":1,"b":2}Worked solution and explanation
This is a key rename dressed up as record consolidation, and the whole question is which dict drives the work. Everyone gets the happy path. The candidates who slip loop over mapping and write record[new] = record.pop(old), quietly assuming every mapped field arrived. Hand that version a record from a system that never sent eml and it raises KeyError instead of returning the fields that did show up, and it mutates the caller's record on the way.
Let the record drive
Step 1: Visit the record's fields, not the mapping's
The output can only contain fields the record actually holds, so those are the keys worth walking. Mapping entries for fields that never arrived are then ignored for free: nothing has to check for them, and nothing can raise.
Step 2: Look each field up with itself as the fallback
mapping.get(key, key) settles both cases in one expression: a mapped field gets its canonical name, an unmapped one gets its own name back. No branch, no membership test, no special case for an empty mapping.
Step 3: Write into a fresh result
Building a new dict leaves the caller's record exactly as it came in, which matters when that raw record is also logged, retried, or compared upstream.
def rename_keys(record, mapping):
result = {}
for key in record:
new_key = mapping.get(key, key)
result[new_key] = record[key]
return result- Driven by the mapping
- Iterating
mapping.items()and movingrecord.pop(old)tonewtouches every mapping entry whether or not the field exists, so an empty record or one missingemlraisesKeyError, and the input dict is rewritten in place. - Driven by the record
- Iterating
recordtouches only fields that exist. Absent mapping entries never come up, unmapped fields pass through via the fallback, and the input is never modified.
Common follow-up questions
- What if two original keys map to the same new key? (Tests conflict detection: raise an error or define last-write-wins behavior.)
- How would you apply the rename to a list of records? (Tests reusing the single-record function across a batch without copying the mapping.)
- What if the mapping is bidirectional and you need to reverse it? (Tests inverting the mapping with `{v: k for k, v in mapping.items()}` and what a non-unique target does to it.)