Skip to content

Commit 0df128c

Browse files
authored
Add Enum.sum_by/2 (#12885)
1 parent e2e38f3 commit 0df128c

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

lib/elixir/lib/enum.ex

+41
Original file line numberDiff line numberDiff line change
@@ -3447,6 +3447,8 @@ defmodule Enum do
34473447
34483448
Raises `ArithmeticError` if `enumerable` contains a non-numeric value.
34493449
3450+
If you need to apply a transformation first, consider using `Enum.sum_by/2` instead.
3451+
34503452
## Examples
34513453
34523454
iex> Enum.sum([1, 2, 3])
@@ -3473,6 +3475,40 @@ defmodule Enum do
34733475
reduce(enumerable, 0, &+/2)
34743476
end
34753477

3478+
@doc """
3479+
Maps and sums the given enumerable in one pass.
3480+
3481+
Raises `ArithmeticError` if `fun` returns a non-numeric value.
3482+
3483+
## Examples
3484+
3485+
iex> Enum.sum_by([%{count: 1}, %{count: 2}, %{count: 3}], fn x -> x.count end)
3486+
6
3487+
3488+
iex> Enum.sum_by(1..3, fn x -> x ** 2 end)
3489+
14
3490+
3491+
iex> Enum.sum_by([], fn x -> x.count end)
3492+
0
3493+
3494+
Filtering can be achieved by returning `0` to ignore elements:
3495+
3496+
iex> Enum.sum_by([1, -2, 3], fn x -> if x > 0, do: x, else: 0 end)
3497+
4
3498+
3499+
"""
3500+
@doc since: "1.18.0"
3501+
@spec sum_by(t, (element -> number)) :: number
3502+
def sum_by(enumerable, mapper)
3503+
3504+
def sum_by(list, mapper) when is_list(list) and is_function(mapper, 1) do
3505+
sum_by_list(list, mapper, 0)
3506+
end
3507+
3508+
def sum_by(enumerable, mapper) when is_function(mapper, 1) do
3509+
reduce(enumerable, 0, fn x, acc -> acc + mapper.(x) end)
3510+
end
3511+
34763512
@doc """
34773513
Returns the product of all elements.
34783514
@@ -4770,6 +4806,11 @@ defmodule Enum do
47704806
{:lists.reverse(acc), []}
47714807
end
47724808

4809+
## sum_by
4810+
4811+
defp sum_by_list([], _, acc), do: acc
4812+
defp sum_by_list([h | t], mapper, acc), do: sum_by_list(t, mapper, acc + mapper.(h))
4813+
47734814
## take
47744815

47754816
defp take_list(_list, 0), do: []

lib/elixir/pages/cheatsheets/enum-cheat.cheatmd

+9
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,15 @@ iex> cart |> Enum.map(& &1.count) |> Enum.sum()
297297
10
298298
```
299299

300+
Note: this should typically be done in one pass using `Enum.sum_by/2`.
301+
302+
### [`sum_by(enum, mapper)`](`Enum.sum_by/2`)
303+
304+
```elixir
305+
iex> Enum.sum_by(cart, & &1.count)
306+
10
307+
```
308+
300309
### [`product(enum)`](`Enum.product/1`)
301310

302311
```elixir

lib/elixir/test/elixir/enum_test.exs

+18
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,24 @@ defmodule EnumTest do
13211321
end
13221322
end
13231323

1324+
test "sum_by/2" do
1325+
assert Enum.sum_by([], &hd/1) == 0
1326+
assert Enum.sum_by([[1]], &hd/1) == 1
1327+
assert Enum.sum_by([[1], [2], [3]], &hd/1) == 6
1328+
assert Enum.sum_by([[1.1], [2.2], [3.3]], &hd/1) == 6.6
1329+
assert Enum.sum_by([[-3], [-2], [-1], [0], [1], [2], [3]], &hd/1) == 0
1330+
1331+
assert Enum.sum_by(1..3, &(&1 ** 2)) == 14
1332+
1333+
assert_raise ArithmeticError, fn ->
1334+
Enum.sum_by([[{}]], &hd/1)
1335+
end
1336+
1337+
assert_raise ArithmeticError, fn ->
1338+
Enum.sum_by([[1], [{}]], &hd/1)
1339+
end
1340+
end
1341+
13241342
test "product/1" do
13251343
assert Enum.product([]) == 1
13261344
assert Enum.product([1]) == 1

0 commit comments

Comments
 (0)