Credence.Pattern.NoZipThenMap
(credence v0.7.0)
Copy Markdown
Detects Enum.zip/2 followed by Enum.map/2 that destructures the
resulting 2-tuples, which can be replaced by Enum.zip_with/3.
Why this matters
Enum.zip(a, b) |> Enum.map(fn {x, y} -> expr end) allocates an
intermediate list of 2-tuples only to immediately destructure them.
Enum.zip_with/3 (available since Elixir 1.14) combines both steps
into a single pass without the intermediate allocation.
Bad
Enum.zip(names, scores)
|> Enum.map(fn {name, score} -> {name, score * 2} end)
Enum.map(Enum.zip(names, scores), fn {name, score} ->
{name, score * 2}
end)
names
|> Enum.zip(scores)
|> Enum.map(fn {name, score} -> {name, score * 2} end)Good
Enum.zip_with(names, scores, fn name, score ->
{name, score * 2}
end)Scope
Flags when ALL of these hold:
Enum.zip/2is called with two arguments.- Its result is passed to
Enum.map/2(via pipe or nesting). - The
Enum.mapfunction destructures a 2-tuple into two variables.
Does NOT flag:
Enum.zip/2alone (no followingEnum.map).Enum.mapwhose function does not destructure a 2-tuple.Enum.zip/1(a list of enumerables) at the head of a pipe — this is the realEnum.zip/1, not a pipe-elidedzip/2, and is not convertible.- Guarded fns (
fn {x, y} when ... -> ... end) — dropping or rebuilding the guard is out of scope, so these are left untouched. - Already-idiomatic
Enum.zip_with/3.
Auto-fix
Replaces the Enum.zip |> Enum.map pair with Enum.zip_with/3,
transforming the fn's tuple pattern into separate parameters.