map_join.ex (2607B)
1 defmodule Credo.Check.Refactor.MapJoin do 2 use Credo.Check, 3 base_priority: :high, 4 explanations: [ 5 check: """ 6 `Enum.map_join/3` is more efficient than `Enum.map/2 |> Enum.join/2`. 7 8 This should be refactored: 9 10 ["a", "b", "c"] 11 |> Enum.map(&String.upcase/1) 12 |> Enum.join(", ") 13 14 to look like this: 15 16 Enum.map_join(["a", "b", "c"], ", ", &String.upcase/1) 17 18 The reason for this is performance, because the two separate calls 19 to `Enum.map/2` and `Enum.join/2` require two iterations whereas 20 `Enum.map_join/3` performs the same work in one pass. 21 """ 22 ] 23 24 @doc false 25 def run(source_file, params \\ []) do 26 issue_meta = IssueMeta.for(source_file, params) 27 28 Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta)) 29 end 30 31 defp traverse( 32 {{:., _, [{:__aliases__, meta, [:Enum]}, :join]}, _, 33 [{{:., _, [{:__aliases__, _, [:Enum]}, :map]}, _, _}, _]} = ast, 34 issues, 35 issue_meta 36 ) do 37 new_issue = issue_for(issue_meta, meta[:line], "map_join") 38 {ast, issues ++ List.wrap(new_issue)} 39 end 40 41 defp traverse( 42 {:|>, meta, 43 [ 44 {{:., _, [{:__aliases__, _, [:Enum]}, :map]}, _, _}, 45 {{:., _, [{:__aliases__, _, [:Enum]}, :join]}, _, _} 46 ]} = ast, 47 issues, 48 issue_meta 49 ) do 50 new_issue = issue_for(issue_meta, meta[:line], "map_join") 51 {ast, issues ++ List.wrap(new_issue)} 52 end 53 54 defp traverse( 55 {{:., meta, [{:__aliases__, _, [:Enum]}, :join]}, _, 56 [ 57 {:|>, _, [_, {{:., _, [{:__aliases__, _, [:Enum]}, :map]}, _, _}]}, 58 _ 59 ]} = ast, 60 issues, 61 issue_meta 62 ) do 63 new_issue = issue_for(issue_meta, meta[:line], "map_join") 64 {ast, issues ++ List.wrap(new_issue)} 65 end 66 67 defp traverse( 68 {:|>, meta, 69 [ 70 {:|>, _, 71 [ 72 _, 73 {{:., _, [{:__aliases__, _, [:Enum]}, :map]}, _, _} 74 ]}, 75 {{:., _, [{:__aliases__, _, [:Enum]}, :join]}, _, _} 76 ]} = ast, 77 issues, 78 issue_meta 79 ) do 80 new_issue = issue_for(issue_meta, meta[:line], "map_join") 81 {ast, issues ++ List.wrap(new_issue)} 82 end 83 84 defp traverse(ast, issues, _issue_meta) do 85 {ast, issues} 86 end 87 88 defp issue_for(issue_meta, line_no, trigger) do 89 format_issue( 90 issue_meta, 91 message: "`Enum.map_join/3` is more efficient than `Enum.map/2 |> Enum.join/2`", 92 trigger: trigger, 93 line_no: line_no 94 ) 95 end 96 end