Batu Lab NotesPractical developer guides

Group records by two selected mapping fields

By Batu ยท English technical notes

Group records by two selected mapping fields

To group Python records by region and status, use the tuple (record["region"], record["status"]) as the group key. Do not make every field in the mapping part of the identity when fields such as note are intentionally ignored.

The fixture has two records with the same region and status but different notes. The failing transformation converts each complete mapping to a tuple of items. That makes the notes part of the key, so it reports four one-record groups. It is a useful contrast because dictionaries themselves are unhashable, and converting all their contents to a hashable representation may avoid that error while still expressing the wrong grouping rule.

The correction selects only the two fields that define a group. It returns three groups: ('north', 'open') has two records, while ('south', 'closed') and ('north', 'closed') each have one. The original list is printed and asserted unchanged; the new result holds references to the original records but does not modify their mappings.

Tuple keys require their elements to be hashable. Missing fields raise KeyError here, which is appropriate for a small fixture but may need validation or a defaulting policy at an input boundary. The standard library used here is available in all maintained Python versions. Dictionary insertion order is guaranteed by the language specification in Python 3.7+, which makes this fixture's printed key order predictable. See mapping types in the Python documentation.

AI assistance disclosure: this article was drafted with AI assistance and its synthetic example was checked for deterministic output.

from collections import defaultdict

records = [
    {"region": "north", "status": "open", "note": "first"},
    {"region": "north", "status": "open", "note": "follow-up"},
    {"region": "south", "status": "closed", "note": "archived"},
    {"region": "north", "status": "closed", "note": "manual"},
]
original_records = [record.copy() for record in records]

# Failure: every field, including note, becomes part of the group identity.
fragmented = defaultdict(int)
for record in records:
    fragmented[tuple(record.items())] += 1

# Correction: select precisely the fields that define a group.
groups = defaultdict(list)
for record in records:
    key = (record["region"], record["status"])
    groups[key].append(record)
counts = {key: len(group) for key, group in groups.items()}

assert records == original_records
assert list(fragmented.values()) == [1, 1, 1, 1]
assert counts == {
    ("north", "open"): 2,
    ("south", "closed"): 1,
    ("north", "closed"): 1,
}

print("input unchanged:", records)
print("full-record group sizes:", list(fragmented.values()))
print("selected-field counts:", counts)
input unchanged: [{'region': 'north', 'status': 'open', 'note': 'first'}, {'region': 'north', 'status': 'open', 'note': 'follow-up'}, {'region': 'south', 'status': 'closed', 'note': 'archived'}, {'region': 'north', 'status': 'closed', 'note': 'manual'}]
full-record group sizes: [1, 1, 1, 1]
selected-field counts: {('north', 'open'): 2, ('south', 'closed'): 1, ('north', 'closed'): 1}