mapDiff<K, V> function

(Map<K, V>, Map<K, V>, Map<K, V>) mapDiff<K, V>(
  1. Map<K, V> before,
  2. Map<K, V> after, {
  3. bool equals(
    1. V a,
    2. V b
    )?,
})

Map diff: keys added, removed, changed. Audited: 2026-06-12 11:26 EDT

Implementation

(Map<K, V> added, Map<K, V> removed, Map<K, V> changed) mapDiff<K, V>(
  Map<K, V> before,
  Map<K, V> after, {
  bool Function(V a, V b)? equals,
}) {
  final Map<K, V> added = <K, V>{};
  final Map<K, V> changed = <K, V>{};
  // Default to `==`; callers pass a custom comparator for deep/semantic equality
  // so unchanged values are not misreported as changed.
  final bool Function(V a, V b) eq = equals ?? (V a, V b) => a == b;
  // First pass over `after`: a key absent from `before` is an addition; a key
  // present in both whose value differs is a change. (Equal values are emitted
  // to neither bucket.) Use key PRESENCE, not a null value, to detect
  // membership: a key whose value is genuinely `null` (nullable V) is a real
  // entry and must still be reported as added/changed. Casting `map[k] as V` is
  // safe because the key is known present from `.keys`/`containsKey`.
  for (final MapEntry<K, V> entry in after.entries) {
    final V afterVal = entry.value;
    if (!before.containsKey(entry.key)) {
      added[entry.key] = afterVal;
    } else {
      // `before[k]` is V? (the lookup is nullable); an `is V` check both narrows
      // it to V and — for a present key — is always true, so a genuine null
      // value (nullable V) is still compared rather than skipped.
      final V? beforeRaw = before[entry.key];
      if (beforeRaw is V && !eq(beforeRaw, afterVal)) changed[entry.key] = afterVal;
    }
  }
  // Second pass over `before`: any key not in `after` is a removal. Removals
  // need their own pass because the first loop only visits `after`'s keys.
  final Map<K, V> removed = <K, V>{};
  for (final MapEntry<K, V> entry in before.entries) {
    if (!after.containsKey(entry.key)) removed[entry.key] = entry.value;
  }
  return (added, removed, changed);
}