Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Python reconciliation exercise: reject duplicate IDs before comparing ledgers

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

Ledger reconciliation compares accepted records by stable identity and reports missing or mismatched entries without silently collapsing duplicates.

Download Python source kit

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

python
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

Output
[('R-0041', 'amount-mismatch'), ('R-0042', 'missing-right'), ('R-0043', 'missing-left')]
duplicate rejected

Costs 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.

python
reconciliation-exercise
Storage details