Ledger reconciliation compares accepted records by stable identity and reports missing or mismatched entries without silently collapsing duplicates.
Python reconciliation exercise: reject duplicate IDs before comparing ledgers
Operation contract
The exercise accepts two owned batches with unique ASCII receipt IDs and bounded exact integer amounts. Each batch is validated into a private mapping before comparison. The result distinguishes missing-left, missing-right and unequal amounts in sorted ID order. A duplicate receipt is rejected instead of allowing last-write-wins dictionary construction to hide it.
Failure and ownership boundary
Reconciliation identifies differences; it does not decide which ledger is authoritative or apply corrective writes. A received ID does not establish permission to inspect the corresponding live record. The batches are bounded to 64 rows each, and input dictionaries remain unchanged. Python aggregation exercise: validate records before returning group totals, Python hash and equality: immutable dictionary keys and Python SQLite job project: reject conflicting replays by request identity describe adjacent invariants.
Working program
import re
def receipt_map(records):
if not isinstance(records, list) or len(records) > 64: raise ValueError("batch rejected")
result = {}
for record in records:
if not isinstance(record, dict) or set(record) != {"id", "amount"}: raise ValueError("fields rejected")
identifier, amount = record["id"], record["amount"]
if not isinstance(identifier, str) or re.fullmatch(r"R-[0-9]{4}", identifier) is None or identifier in result: raise ValueError("identifier rejected")
if type(amount) is not int or not 0 <= amount <= 1000000: raise ValueError("amount rejected")
result[identifier] = amount
return result
def reconcile_receipts(left_records, right_records):
left, right = receipt_map(left_records), receipt_map(right_records)
differences = []
for identifier in sorted(left.keys() | right.keys()):
if identifier not in left: differences.append((identifier, "missing-left"))
elif identifier not in right: differences.append((identifier, "missing-right"))
elif left[identifier] != right[identifier]: differences.append((identifier, "amount-mismatch"))
return differences
left = [{"id":"R-0041","amount":125},{"id":"R-0042","amount":250}]
right = [{"id":"R-0041","amount":150},{"id":"R-0043","amount":75}]
print(reconcile_receipts(left, right))
try: reconcile_receipts(left + [left[0].copy()], right)
except ValueError: print("duplicate rejected")Output
[('R-0041', 'amount-mismatch'), ('R-0042', 'missing-right'), ('R-0043', 'missing-left')]
duplicate rejectedCosts and limits
For n and m accepted records, expected validation/mapping work is O(n+m); sorting union keys costs O(k log k). The result and private maps retain O(n+m) state. This bounded exercise has no live database or corrective side effects.
Common Mistakes
- Duplicate identities are rejected input, not an ordinary overwrite.
- Report differences before defining or executing corrective policy.
Connected lessons
Python aggregation exercise: validate records before returning group totals, Python hash and equality: immutable dictionary keys, Python SQLite job project: reject conflicting replays by request identity.
Follow the service contract
Python expiry exercise: separate pending, active and expired records at exact boundaries.
Check this related boundary
Python exercise: reconcile duplicate receipt counts without losing multiplicity.
