fluttersmith/py-snippetsGitHub ↗

Invert a dictionary, keeping every key

Invert a dict so each value maps to the list of keys that had it.

Dicts#mapping#groupingO(n) time✓ 2 examples pass on 3.11–3.14

Who is 10 years old? Inverting {name: age} with a plain comprehension keeps only one name per age. This keeps all of them.

collect_dictionary.py✓ ruff · mypy --strict
from collections import defaultdict
from collections.abc import Hashable, Mapping
from typing import TypeVar

K = TypeVar("K")
V = TypeVar("V", bound=Hashable)


def collect_dictionary(d: Mapping[K, V]) -> dict[V, list[K]]:
    """Invert ``d``, collecting every key that shares a value into a list."""
    inverted: defaultdict[V, list[K]] = defaultdict(list)
    for key, value in d.items():
        inverted[value].append(key)
    return dict(inverted)
  1. keys stay in d's order inside each list
examples✓ 2 doctests · 3.11–3.14
passed on Python 3.11 to 3.14: >>> collect_dictionary({"Peter": 10, "Isabel": 10, "Anna": 9}){10: ['Peter', 'Isabel'], 9: ['Anna']}passed on Python 3.11 to 3.14: >>> collect_dictionary({}){}
Edit the function or the examples and run the doctests here, on Python 3.14. Python loads only when you click (about 7 MB, cached after the first time).

How it works

  • defaultdict(list) starts an empty list for each new value.
  • Keys keep the dict’s order inside each list.
  • It’s group_by applied to a dict’s items.

Complexity: O(n) time, O(n) space

Importfrom pysnippets.dicts.collect_dictionary import collect_dictionaryNo dependencies: copy collect_dictionary.py into your project, or clone the repo.

Adapted from collect-dictionary in 30-seconds-of-python by the 30 seconds of code contributors, CC BY 4.0. Changed: Typed; returns a plain dict. Suggest an edit.