Credence.Pattern.AvoidGraphemesEnumCountWithPredicate
(credence v0.7.1)
Copy Markdown
Performance rule: Detects Enum.count/2 with an equality predicate or
Enum.sum_by/2 with a counting function on the result of String.graphemes/1.
Splitting a string into a grapheme list just to count occurrences of a
specific character is wasteful. String.count/2 performs the same count
directly on the string without allocating the intermediate list.
Safety: behind a switch
The rewrite is identical to the original only while every character in the
running data is a single codepoint — otherwise String.count/2 (which scans
raw text) and the grapheme comparison disagree on a decomposed accent. So this
rule needs single_codepoint_graphemes (see Credence.Assumptions) and runs
only while that promise is on.
Two guards keep the rewrite honest (decision 6a — shrink first, then promise):
- The compared literal is narrowed to a single codepoint
(
length(String.to_charlist(lit)) == 1), dropping"","ab", and the two-codepoint form of"é". After that narrowing the only remaining difference between old and new is the rare decomposed-accent case in the data — which is exactly what the promise covers. - A shared
single_codepoint?/1helper gates bothcheckandfix, so they can never disagree on which literals qualify.
Bad
String.graphemes(str) |> Enum.count(&(&1 == "1"))
Enum.count(String.graphemes(str), &(&1 == "1"))
str |> String.graphemes() |> Enum.count(fn c -> c == "1" end)
String.graphemes(str) |> Enum.sum_by(fn "1" -> 1; _ -> 0 end)
Enum.sum_by(String.graphemes(str), fn "1" -> 1; _ -> 0 end)Good
String.count(str, "1")
str |> String.count("1")