Credence.Pattern.NoMapKeysOrValuesForIteration
(credence v0.7.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)
# 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)Enum.sum, Enum.product, Enum.max, and Enum.min with
Map.values/Map.keys are already idiomatic and not flagged.
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)
Map.values(map) |> Enum.sum() # already idiomatic
Enum.max(Map.values(m)) # already idiomaticGlossary (terms used throughout this module)
map_fn: theMapfunction being eliminated, either:keysor`:values`. Drives every `{k, v}` slot decision.enum_fn: theEnumfunction the user is calling on the result of`Map.keys/values`.map_expr: the AST of the actual map being iterated (the argumentto `Map.keys/values`).enum_args/enum_args: the remaining args of theEnumcall(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)weappend when the inner Enum function returns a list of `{k, v}` pairs but the caller only wanted keys/values.