A dict-of-lists in wide/nested format unrolled into flat (category, item) row tuples with a nested loop. The replay shows k and vs loading each outer key in turn, then v stepping through its values — one (k, v) pair per inner iteration.

By hand

The Pythonic way

A nested list comprehension mirrors the two loops in a single expression. The outer clause for k, vs in data.items() iterates keys; the inner clause for v in vs iterates each value list.

naive.py
data = {'fruit': ['apple', 'pear'], 'veg': ['carrot', 'pea'], 'grain': ['oat', 'rye']}
rows = []
# trace: ignore rows
for k, vs in data.items():
    for v in vs:
        rows.append((k, v))
print('RESULT:', (len(rows), rows[-1]))
library.py
data = {'fruit': ['apple', 'pear'], 'veg': ['carrot', 'pea'], 'grain': ['oat', 'rye']}
rows = [(k, v) for k, vs in data.items() for v in vs]
print('RESULT:', (len(rows), rows[-1]))
RESULT: (6, ('grain', 'rye'))

Implementation notes

  • This is the inverse of pivot-long-to-wide: where that lesson folded long (key, attr, val) rows into a nested dict-of-dicts, this lesson unfolds a nested dict-of-lists back into long (category, item) rows.
  • dict.items() returns key-value pairs in insertion order (guaranteed since Python 3.7). If the input order is not meaningful, sort the output with sorted(rows) for a deterministic result.
  • In pandas, df.melt(id_vars=[...], value_vars=[...]) performs the wide-to- long transformation. The pandas melt lesson (roadmap) covers aggregation and column naming in the melted result.