nested dictionaries
key compression
dictionary flattening
data structure manipulation
Python programming

Flatten nested dictionaries, compressing keys

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Flattening nested dictionaries is useful for logging, analytics pipelines, CSV export, and configuration normalization. The usual goal is to turn hierarchical keys into a single-level mapping like parent.child.leaf: value. The challenge is preserving enough structure to avoid collisions while still producing compact keys. You also need a predictable strategy for lists inside dictionaries and for non-string keys. A robust flattening function should be deterministic, configurable, and reversible when needed. This article covers practical patterns for compressing nested keys in Python with recursion and clear delimiter rules.

Core Sections

1. Basic recursive flattening

A standard approach traverses the dictionary recursively and joins key segments.

python
1def flatten_dict(data, parent="", sep="."):
2    out = {}
3    for k, v in data.items():
4        key = f"{parent}{sep}{k}" if parent else str(k)
5        if isinstance(v, dict):
6            out.update(flatten_dict(v, key, sep=sep))
7        else:
8            out[key] = v
9    return out
10
11nested = {"user": {"id": 7, "name": "Ada"}, "active": True}
12print(flatten_dict(nested))
13# {'user.id': 7, 'user.name': 'Ada', 'active': True}

This is concise and works for most dictionary-only structures.

2. Handling lists in nested payloads

APIs frequently return arrays inside objects. Include list indexes in keys.

python
1def flatten_any(data, parent="", sep="."):
2    out = {}
3    if isinstance(data, dict):
4        for k, v in data.items():
5            key = f"{parent}{sep}{k}" if parent else str(k)
6            out.update(flatten_any(v, key, sep))
7    elif isinstance(data, list):
8        for i, v in enumerate(data):
9            key = f"{parent}{sep}{i}" if parent else str(i)
10            out.update(flatten_any(v, key, sep))
11    else:
12        out[parent] = data
13    return out

This preserves positional context and avoids data loss.

3. Prevent key collisions

Collision example: original key contains the separator (.). Use escaping, a rare separator, or tuple keys internally.

python
safe = flatten_dict(data, sep="__")

If round-tripping is required, define escaping rules explicitly and test them.

4. Compression trade-offs

Short key formats save storage but reduce readability. For observability logs, readable keys are often worth the extra bytes. For high-volume telemetry, compact delimiters and optional key maps can reduce payload size.

5. Performance and memory strategy

Recursive flattening creates new dict entries for every leaf. For very large objects, consider a generator that yields (key, value) pairs and stream into sinks rather than building one giant dictionary.

python
1def iter_flat(data, parent="", sep="."):
2    if isinstance(data, dict):
3        for k, v in data.items():
4            key = f"{parent}{sep}{k}" if parent else str(k)
5            yield from iter_flat(v, key, sep)
6    else:
7        yield parent, data

This supports memory-conscious pipelines.

Validation and production readiness

A reliable implementation should include more than a working snippet. Add a small reproducible dataset or input fixture that exercises expected behavior and edge cases, then codify it in automated tests. Include at least one “happy path,” one malformed input case, and one boundary condition so regressions are caught early. Instrument key steps with structured logs or metrics to make failures diagnosable in runtime environments, not just local development. If performance is relevant, keep a lightweight benchmark that can be rerun after refactors to ensure behavior stays within budget.

Operationally, document assumptions near the code: required library versions, environment variables, timezone/locale expectations, and failure handling strategy. For team workflows, add one integration test that mirrors real usage rather than only unit-level checks. This reduces drift between example code and production behavior. Treat these checks as part of feature completion, because most long-term issues are caused by unvalidated assumptions rather than syntax errors.

Common Pitfalls

  • Flattening without a collision strategy for keys containing delimiters.
  • Ignoring lists and accidentally dropping nested array values.
  • Assuming all keys are strings and failing on numeric or mixed key types.
  • Creating irreversible flattened forms when downstream requires reconstruction.
  • Building massive intermediate dicts instead of streaming key-value pairs.

Summary

Flattening nested dictionaries is straightforward when rules are explicit: delimiter choice, list indexing behavior, and collision handling. Recursive functions provide clean implementations, while generator-based variants help with large payloads. Choose readability or compactness based on downstream needs, and add tests for edge cases like delimiter collisions and list-heavy structures. With those guardrails, flattened-key representations remain reliable and scalable.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.