Credence.Pattern.NoMapKeysEnumLookup (credence v0.8.0)

Copy Markdown

Detects Map.keys(var) piped into an Enum function whose callback also looks up values from the same map variable.

Why this matters

LLMs frequently port the Python idiom for key in dict: ... dict[key] into Elixir as Map.keys(map) |> Enum.xxx(fn k -> ... map[k] ... end). This creates an unnecessary intermediate list and performs redundant lookups. In Elixir, maps are directly enumerable as {key, value} pairs:

# Flagged — extra allocation + redundant lookups
Map.keys(freqs)
|> Enum.all?(fn char -> Map.get(other, char, 0) >= freqs[char] end)

# Idiomatic — single traversal, values already in hand
Enum.all?(freqs, fn {char, count} ->
  Map.get(other, char, 0) >= count
end)

Detection scope (strict)

Only flagged when all three conditions hold:

  1. Map.keys(var) is called on a simple variable,
  2. The result is passed to Enum.all? or Enum.any?, and
  3. The callback body references var via var[key], Map.get(var, ...), Map.fetch(var, ...), or Map.fetch!(var, ...).

Patterns where only keys are needed (no value lookup in the callback) are not flagged.

Only all?/any? are flagged because their result is a boolean — a commutative AND/OR over the elements — so iterating the map directly (a different order than Map.keys/1 once a map exceeds 32 entries) gives the same answer. map/filter/reject/flat_map return order-observable lists and each's side effects are order-sensitive, so rewriting them would not be behaviour-preserving and they are left alone.