Credence.Pattern.NoMapKeysOrValuesForIteration
(credence v0.8.0)
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.
Scope — only order-INDEPENDENT terminals are rewritten
Map.keys/1 and Map.values/1 iterate in a different order than a direct
Enum-over-map traversal once the map has more than 32 keys (small-map array
vs. hash-tree iterator). So the rule rewrites ONLY operations whose result is
independent of iteration order — all?, any?, count, empty?,
frequencies, frequencies_by. Order-dependent ops (map, filter, find,
at, take, join, reduce, sort, …) would reorder the result and are
left untouched: neither flagged nor rewritten (check and fix share one scope
gate, fixable?/2). Enum.sum/product/max/min are already idiomatic and
also not flagged.
Automatic fixing
# Callback wrapping binds the user's variable to the right slot:
Enum.all?(Map.values(degrees), fn v -> v == 0 end)
→ Enum.all?(degrees, fn {_k, v} -> v == 0 end)
Map.values(m) |> Enum.count()
→ Enum.count(m)
Enum.frequencies(Map.keys(m))
→ Enum.frequencies_by(m, fn {k, _} -> k end)Bad
Enum.all?(Map.values(degrees), fn v -> v == 0 end)
Map.values(map) |> Enum.count()Good
Enum.all?(degrees, fn {_k, v} -> v == 0 end)
Enum.count(map)
Map.values(map) |> Enum.sum() # already idiomatic
Map.values(m) |> Enum.filter(...) # order-dependent — left aloneGlossary (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.