ForgeCredoChecks.MapRejectNil (forge_credo_checks v0.4.0)

Copy Markdown View Source

Basics

This check is disabled by default.

Learn how to enable it via .credo.exs.

This check has a base priority of high and works with any version of Elixir.

Explanation

Replace Enum.map/2 |> Enum.reject(&is_nil/1) with a comprehension or Enum.flat_map/2.

Why

The pipe walks the list twice and allocates an intermediate list that contains the nils that will be discarded next. A comprehension or flat_map fuses both steps in one pass, preserves order naturally, and avoids the reduce + reverse anti-pattern.

How to fix (in order of preference)

Preferred: comprehension. A = binding inside the comprehension computes the value once and filters on it:

# BEFORE
things
|> Enum.map(&parse/1)
|> Enum.reject(&is_nil/1)

# AFTER
for x <- things, v = parse(x), not is_nil(v), do: v

One pass, in-order, no intermediate list, no reverse step.

Also good: Enum.flat_map/2. Especially natural when parse/1 conceptually returns "0-or-1 results":

Enum.flat_map(things, fn x ->
  case parse(x) do
    nil -> []
    v -> [v]
  end
end)

Also one pass, in-order, no reverse.

Last resort: Enum.reduce/3. Only when neither shape fits and the consumer does not care about order:

Enum.reduce(things, [], fn x, acc ->
  case parse(x) do
    nil -> acc
    v -> [v | acc]
  end
end)

What NOT to do

Do not switch to Enum.reduce/3 and append |> Enum.reverse/1 to restore order. That second pass is exactly the cost the comprehension and flat_map exist to avoid.

Check-Specific Parameters

There are no specific parameters for this check.

General Parameters

Like with all checks, general params can be applied.

Parameters can be configured via the .credo.exs config file.