Credence.Pattern.NoMapKeysOrValuesForIteration (credence v0.6.1)

Copy Markdown

Performance rule: Detects Map.values(map) or Map.keys(map) passed directly into an Enum function, which creates an unnecessary intermediate list.

All Enum functions accept maps directly and iterate over {key, value} pairs without allocating an intermediate list.

Automatic fixing

# Callback wrapping
Enum.all?(Map.values(degrees), fn v -> v == 0 end)
 Enum.all?(degrees, fn {_k, v} -> v == 0 end)

# max/min → max_by/min_by + elem
Enum.max(Map.values(m))
 Enum.max_by(m, fn {_k, v} -> v end) |> elem(1)

# sum/product → reduce
Enum.sum(Map.values(m))
 Enum.reduce(m, 0, fn {_k, v}, acc -> acc + v end)

# find/at → case expression
Enum.find(Map.values(m), fn v -> v > 0 end)
 case Enum.find(m, fn {_k, v} -> v > 0 end) do
    nil -> nil; {_, v} -> v
  end

# filter/sort/etc → chain with Enum.map
Map.keys(m) |> Enum.filter(fn k -> k > 0 end)
 m |> Enum.filter(fn {k, _v} -> k > 0 end)
  |> Enum.map(fn {k, _v} -> k end)

Functions returning complex structures (chunk_every, zip, split, with_index, scan, tally, etc.) cannot be safely auto-fixed and are handled by NoMapKeysOrValuesForRawIteration.

Bad

Enum.all?(Map.values(degrees), fn v -> v == 0 end)
Map.keys(map) |> Enum.map(&to_string/1)

Good

Enum.all?(degrees, fn {_k, v} -> v == 0 end)
Enum.map(map, fn {k, _v} -> to_string(k) end)

Glossary (terms used throughout this module)

  • map_fn : the Map function being eliminated, either :keys or
            `:values`. Drives every `{k, v}` slot decision.
  • enum_fn : the Enum function the user is calling on the result of
            `Map.keys/values`.
  • map_expr: the AST of the actual map being iterated (the argument
            to `Map.keys/values`).
  • enum_args / enum_args: the remaining args of the Enum call
            (everything after `Map.keys/values(map)`).
  • "kv pattern" : the destructuring tuple pattern ({k, _v} or
            `{_k, v}`) we substitute for the user's single-arg
            callback parameter.
  • "extractor": the closing Enum.map(..., fn {k, _} -> k end) we
            append when the inner Enum function returns a list of
            `{k, v}` pairs but the caller only wanted keys/values.