Credence.Pattern.NoUniqThenCount
(credence v0.7.0)
Copy Markdown
Detects Enum.uniq/1 piped into length/1 or Enum.count/1, which
creates an unnecessary intermediate list only to count its size.
Why this matters
LLMs frequently produce Enum.uniq |> length() pipelines to count
distinct elements. This allocates an intermediate list that is
immediately consumed by a linear scan:
# Flagged — intermediate list from uniq
items |> Enum.uniq() |> length()
items |> Enum.uniq() |> Enum.count()
# Better — MapSet deduplicates on insertion, no intermediate list
items |> MapSet.new() |> MapSet.size()MapSet.new/1 builds the set in a single pass, and MapSet.size/1
returns the count in O(1).
Why the rewrite is exact
Enum.uniq/1 and MapSet both deduplicate using map-key (exact,
===) equality — 1 and 1.0 stay distinct under both, +0.0 and
-0.0 stay distinct under both. The number of distinct elements is
therefore identical, and length/1/Enum.count/1 and MapSet.size/1
all return that cardinality. The source enumerable is evaluated exactly
once on the left of the pipe in both forms.
Flagged patterns
Enum.uniq() piped into length/1 or Enum.count/1, in two forms:
- piped uniq:
enum |> Enum.uniq() |> length() - head uniq:
Enum.uniq(enum) |> Enum.count()
Not flagged
Enum.uniq()alone (without count/length following)Enum.uniq |> Enum.map(...)(uniq used for further processing)Enum.uniq |> Enum.count(predicate)(a filtered count, different op)