fluttersmith/py-snippetsGitHub ↗

List union by a key

Merge two lists, keeping the first item for each key, in a predictable order.

Lists#sets#dedupeO(n + m) time✓ 3 examples pass on 3.11–3.14

Merge two contact lists without duplicates, where two entries match when their emails match in any case.

union_by.py✓ ruff · mypy --strict
from collections.abc import Callable, Hashable, Iterable
from typing import TypeVar

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


def union_by(a: Iterable[T], b: Iterable[T], key: Callable[[T], K]) -> list[T]:
    """Merge ``a`` and ``b``, keeping the first item for each ``key``, in order."""
    seen: set[K] = set()
    merged = []
    for items in (a, b):
        for item in items:
            k = key(item)
            if k not in seen:
                seen.add(k)
                merged.append(item)
    return merged
  1. first one wins, so a beats b on a tie
examples✓ 3 doctests · 3.11–3.14
passed on Python 3.11 to 3.14: >>> from math import floorpassed on Python 3.11 to 3.14: >>> union_by([2.1], [1.2, 2.3], floor)[2.1, 1.2]passed on Python 3.11 to 3.14: >>> union_by(["Ada", "bob"], ["ADA", "Cy"], str.lower)['Ada', 'bob', 'Cy']
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

  • A set of seen keys decides whether each item is new.
  • Items of a come first, then the new items of b, each in their original order.
  • Duplicates inside a are dropped too.

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

Importfrom pysnippets.lists.union_by import union_byNo dependencies: copy union_by.py into your project, or clone the repo.

Adapted from union-by in 30-seconds-of-python by the 30 seconds of code contributors, CC BY 4.0. Changed: Rewritten: deterministic order, first item wins; typed. Suggest an edit.