Credence.Pattern.PreferMapNewWithTransform
(credence v0.8.0)
Copy Markdown
Detects Enum.map/2 followed by Map.new/0 and suggests combining them
into a single Map.new/2 call.
Why this matters
Enum.map(coll, fn ...) |> Map.new() allocates an intermediate list of
2-tuples only to immediately convert them into a map. Map.new/2 combines
both steps into a single pass without the intermediate allocation, and is
clearer about intent.
Bad
Enum.map(1..5, fn i -> {i, i * i} end) |> Map.new()
Map.new(Enum.map(1..5, fn i -> {i, i * i} end))
1..5
|> Enum.map(fn i -> {i, i * i} end)
|> Map.new()Good
Map.new(1..5, fn i -> {i, i * i} end)When the Enum.map is fed by an upstream pipeline, that pipeline is kept and
the map step folds into a piped Map.new/2 (the collection stays in the pipe):
# Bad
list
|> filter_keys()
|> Enum.map(fn {k, v} -> {k, f(v)} end)
|> Map.new()
# Good
list
|> filter_keys()
|> Map.new(fn {k, v} -> {k, f(v)} end)Scope
Flags when ALL of these hold:
Enum.map/2is called with two arguments (enumerable + function).- Its result is passed to
Map.new/0(via pipe or nesting). - The
Map.newcall has zero arguments (i.e., it'sMap.new/0, notMap.new/1).
Does NOT flag:
Enum.map/2alone (no followingMap.new).Map.new/1(with an explicit enumerable arg).Enum.map/2with a function that doesn't return 2-tuples (we don't check this — the pattern match is syntactic only).
Auto-fix
Replaces the Enum.map |> Map.new pair with Map.new/2, moving the
enumerable and function into the Map.new call.