From 7644fd6f44e6cc5f069b629c5a25bcdc873defb9 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Tue, 22 Aug 2023 23:26:21 +0900 Subject: [PATCH 1/9] Add Enum.sum/2 --- lib/elixir/lib/enum.ex | 23 +++++++++++++++++++++++ lib/elixir/test/elixir/enum_test.exs | 18 ++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index 66851044b6f..19628bccb06 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3473,6 +3473,29 @@ defmodule Enum do reduce(enumerable, 0, &+/2) end + @doc """ + Maps and sums the given enumerable in one pass. + + Raises `ArithmeticError` if `fun` returns a non-numeric value. + + ## Examples + + iex> Enum.sum([%{count: 1}, %{count: 2}, %{count: 3}], fn x -> x.count end) + 6 + + iex> Enum.sum(1..3, fn x -> x ** 2 end) + 14 + + iex> Enum.sum([], fn x -> x.count end) + 0 + + """ + @doc since: "1.16.0" + @spec sum(t, (element -> number)) :: number + def sum(enumerable, fun) when is_function(fun, 1) do + reduce(enumerable, 0, fn x, acc -> acc + fun.(x) end) + end + @doc """ Returns the product of all elements. diff --git a/lib/elixir/test/elixir/enum_test.exs b/lib/elixir/test/elixir/enum_test.exs index 42fb0f7d8ba..960624e142d 100644 --- a/lib/elixir/test/elixir/enum_test.exs +++ b/lib/elixir/test/elixir/enum_test.exs @@ -1321,6 +1321,24 @@ defmodule EnumTest do end end + test "sum/2" do + assert Enum.sum([], &hd/1) == 0 + assert Enum.sum([[1]], &hd/1) == 1 + assert Enum.sum([[1], [2], [3]], &hd/1) == 6 + assert Enum.sum([[1.1], [2.2], [3.3]], &hd/1) == 6.6 + assert Enum.sum([[-3], [-2], [-1], [0], [1], [2], [3]], &hd/1) == 0 + + assert Enum.sum(1..3, &(&1 ** 2)) == 14 + + assert_raise ArithmeticError, fn -> + Enum.sum([[{}]], &hd/1) + end + + assert_raise ArithmeticError, fn -> + Enum.sum([[1], [{}]], &hd/1) + end + end + test "product/1" do assert Enum.product([]) == 1 assert Enum.product([1]) == 1 From b2e4005c89fc3ca8f13c601dc0ed6e2c69077580 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Wed, 23 Aug 2023 08:03:38 +0900 Subject: [PATCH 2/9] Add specialized implementation for lists --- lib/elixir/lib/enum.ex | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index 19628bccb06..dd6a4390701 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3492,6 +3492,12 @@ defmodule Enum do """ @doc since: "1.16.0" @spec sum(t, (element -> number)) :: number + def sum(enumerable, fun) + + def sum(list, fun) when is_list(list) and is_function(fun, 1) do + sum_list(list, fun, 0) + end + def sum(enumerable, fun) when is_function(fun, 1) do reduce(enumerable, 0, fn x, acc -> acc + fun.(x) end) end @@ -4793,6 +4799,11 @@ defmodule Enum do {:lists.reverse(acc), []} end + ## sum + + defp sum_list([], _, acc), do: acc + defp sum_list([h | t], fun, acc), do: sum_list(t, fun, acc + fun.(h)) + ## take defp take_list(_list, 0), do: [] From 3682c2e16187a01595e06cf3b86a3fe3153d7c57 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Tue, 5 Sep 2023 01:01:23 +0900 Subject: [PATCH 3/9] Document filtering in Enum.sum/2 --- lib/elixir/lib/enum.ex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index dd6a4390701..dee6fca80e8 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3489,6 +3489,11 @@ defmodule Enum do iex> Enum.sum([], fn x -> x.count end) 0 + Filtering can be achieved by returning `0` to remove elements: + + iex> Enum.sum([1, -2, 3], fn x -> if x > 0, do: x, else: 0 end) + 4 + """ @doc since: "1.16.0" @spec sum(t, (element -> number)) :: number From 570887c02fec7f4d48dc9a55baaf4cc2542989b8 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Fri, 21 Jun 2024 23:42:57 +0900 Subject: [PATCH 4/9] Rename to sum_by --- lib/elixir/lib/enum.ex | 24 ++++++++++++------------ lib/elixir/test/elixir/enum_test.exs | 18 +++++++++--------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index dee6fca80e8..8267f8c217d 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3480,30 +3480,30 @@ defmodule Enum do ## Examples - iex> Enum.sum([%{count: 1}, %{count: 2}, %{count: 3}], fn x -> x.count end) + iex> Enum.sum_by([%{count: 1}, %{count: 2}, %{count: 3}], fn x -> x.count end) 6 - iex> Enum.sum(1..3, fn x -> x ** 2 end) + iex> Enum.sum_by(1..3, fn x -> x ** 2 end) 14 - iex> Enum.sum([], fn x -> x.count end) + iex> Enum.sum_by([], fn x -> x.count end) 0 Filtering can be achieved by returning `0` to remove elements: - iex> Enum.sum([1, -2, 3], fn x -> if x > 0, do: x, else: 0 end) + iex> Enum.sum_by([1, -2, 3], fn x -> if x > 0, do: x, else: 0 end) 4 """ @doc since: "1.16.0" - @spec sum(t, (element -> number)) :: number - def sum(enumerable, fun) + @spec sum_by(t, (element -> number)) :: number + def sum_by(enumerable, fun) - def sum(list, fun) when is_list(list) and is_function(fun, 1) do - sum_list(list, fun, 0) + def sum_by(list, fun) when is_list(list) and is_function(fun, 1) do + sum_by_list(list, fun, 0) end - def sum(enumerable, fun) when is_function(fun, 1) do + def sum_by(enumerable, fun) when is_function(fun, 1) do reduce(enumerable, 0, fn x, acc -> acc + fun.(x) end) end @@ -4804,10 +4804,10 @@ defmodule Enum do {:lists.reverse(acc), []} end - ## sum + ## sum_by - defp sum_list([], _, acc), do: acc - defp sum_list([h | t], fun, acc), do: sum_list(t, fun, acc + fun.(h)) + defp sum_by_list([], _, acc), do: acc + defp sum_by_list([h | t], fun, acc), do: sum_by_list(t, fun, acc + fun.(h)) ## take diff --git a/lib/elixir/test/elixir/enum_test.exs b/lib/elixir/test/elixir/enum_test.exs index 960624e142d..a2aba41ab92 100644 --- a/lib/elixir/test/elixir/enum_test.exs +++ b/lib/elixir/test/elixir/enum_test.exs @@ -1321,21 +1321,21 @@ defmodule EnumTest do end end - test "sum/2" do - assert Enum.sum([], &hd/1) == 0 - assert Enum.sum([[1]], &hd/1) == 1 - assert Enum.sum([[1], [2], [3]], &hd/1) == 6 - assert Enum.sum([[1.1], [2.2], [3.3]], &hd/1) == 6.6 - assert Enum.sum([[-3], [-2], [-1], [0], [1], [2], [3]], &hd/1) == 0 + test "sum_by/2" do + assert Enum.sum_by([], &hd/1) == 0 + assert Enum.sum_by([[1]], &hd/1) == 1 + assert Enum.sum_by([[1], [2], [3]], &hd/1) == 6 + assert Enum.sum_by([[1.1], [2.2], [3.3]], &hd/1) == 6.6 + assert Enum.sum_by([[-3], [-2], [-1], [0], [1], [2], [3]], &hd/1) == 0 - assert Enum.sum(1..3, &(&1 ** 2)) == 14 + assert Enum.sum_by(1..3, &(&1 ** 2)) == 14 assert_raise ArithmeticError, fn -> - Enum.sum([[{}]], &hd/1) + Enum.sum_by([[{}]], &hd/1) end assert_raise ArithmeticError, fn -> - Enum.sum([[1], [{}]], &hd/1) + Enum.sum_by([[1], [{}]], &hd/1) end end From d6e5581c323020dad01fe31592c2d8996c0553da Mon Sep 17 00:00:00 2001 From: sabiwara Date: Fri, 21 Jun 2024 23:47:06 +0900 Subject: [PATCH 5/9] Add Enum.sum_by/2 to cheatsheet --- lib/elixir/pages/cheatsheets/enum-cheat.cheatmd | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd b/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd index 73255de6c11..d742abeb748 100644 --- a/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd +++ b/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd @@ -297,6 +297,15 @@ iex> cart |> Enum.map(& &1.count) |> Enum.sum() 10 ``` +Note: this should typically be done in one pass using `Enum.sum_by/2`. + +### [`sum_by(enum, mapper)`](`Enum.sum_by/2`) + +```elixir +iex> Enum.sum_by(cart, & &1.count) +10 +``` + ### [`product(enum)`](`Enum.product/1`) ```elixir From a226435fdafa540799a473827bf5f99b1fae2350 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Fri, 21 Jun 2024 23:54:09 +0900 Subject: [PATCH 6/9] Mention in doc --- lib/elixir/lib/enum.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index 8267f8c217d..16b57e6067b 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3447,6 +3447,8 @@ defmodule Enum do Raises `ArithmeticError` if `enumerable` contains a non-numeric value. + If you need to apply a transformation first, consider using `Enum.sum_by/2` instead. + ## Examples iex> Enum.sum([1, 2, 3]) From c469fe7ff4027d48b84104eb9f7ca95fa50c6d1b Mon Sep 17 00:00:00 2001 From: sabiwara Date: Fri, 21 Jun 2024 23:54:24 +0900 Subject: [PATCH 7/9] Rename argument fun -> mapper --- lib/elixir/lib/enum.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index 16b57e6067b..c1298e8c69f 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3499,14 +3499,14 @@ defmodule Enum do """ @doc since: "1.16.0" @spec sum_by(t, (element -> number)) :: number - def sum_by(enumerable, fun) + def sum_by(enumerable, mapper) - def sum_by(list, fun) when is_list(list) and is_function(fun, 1) do - sum_by_list(list, fun, 0) + def sum_by(list, mapper) when is_list(list) and is_function(mapper, 1) do + sum_by_list(list, mapper, 0) end - def sum_by(enumerable, fun) when is_function(fun, 1) do - reduce(enumerable, 0, fn x, acc -> acc + fun.(x) end) + def sum_by(enumerable, mapper) when is_function(mapper, 1) do + reduce(enumerable, 0, fn x, acc -> acc + mapper.(x) end) end @doc """ @@ -4809,7 +4809,7 @@ defmodule Enum do ## sum_by defp sum_by_list([], _, acc), do: acc - defp sum_by_list([h | t], fun, acc), do: sum_by_list(t, fun, acc + fun.(h)) + defp sum_by_list([h | t], mapper, acc), do: sum_by_list(t, mapper, acc + mapper.(h)) ## take From c7f1cf69d3890a5bc3e009c943b9ce6523e8bfcd Mon Sep 17 00:00:00 2001 From: Jean Klingler Date: Sat, 22 Jun 2024 00:02:59 +0900 Subject: [PATCH 8/9] Update :since doc Co-authored-by: Wojtek Mach --- lib/elixir/lib/enum.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index c1298e8c69f..f44fe45e90a 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3497,7 +3497,7 @@ defmodule Enum do 4 """ - @doc since: "1.16.0" + @doc since: "1.18.0" @spec sum_by(t, (element -> number)) :: number def sum_by(enumerable, mapper) From abb5589406b75b9c70a6dea120ec499694228bce Mon Sep 17 00:00:00 2001 From: Jean Klingler Date: Sat, 22 Jun 2024 06:20:55 +0900 Subject: [PATCH 9/9] Use better wording in docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ɓukasz Jan Niemier --- lib/elixir/lib/enum.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex index f44fe45e90a..5b919bf45b5 100644 --- a/lib/elixir/lib/enum.ex +++ b/lib/elixir/lib/enum.ex @@ -3491,7 +3491,7 @@ defmodule Enum do iex> Enum.sum_by([], fn x -> x.count end) 0 - Filtering can be achieved by returning `0` to remove elements: + Filtering can be achieved by returning `0` to ignore elements: iex> Enum.sum_by([1, -2, 3], fn x -> if x > 0, do: x, else: 0 end) 4