Reputation: 10876
Given:
data = [[1,2,3, ..., n],[1,2,3, ..., n],[1,2,3, ..., n], ...]
# List with N rows of equal length
How may we get a row sum: [3,6,9, ..., x]
It is not immediately obvious which of the Enum
functions to use, or how to hold a running sum using list comprehension
Upvotes: 0
Views: 464
Reputation: 121010
I would say, the most readable way would be:
data
|> Enum.zip()
|> Enum.map(fn {v1, v2} -> v1 + v2 end)
#⇒ [2, 4, 6, ..., x]
For the case of N
lists, there is a recursion to be used:
data = [[1,2,3],[1,2,3],[1,2,3]]
defmodule ListSum do
def mapper([inner1 | [inner2 | rest]]) do
reduced = inner1
|> Enum.zip(inner2)
|> Enum.map(fn {v1, v2} -> v1 + v2 end)
mapper([reduced | rest])
end
def mapper([list]) when is_list(list), do: list
end
IO.inspect ListSum.mapper(data)
#⇒ [3, 6, 9]
The thing is in Erlang/Elixir the easiest approach to extend the solution to a list of inputs is to recursively simplify anything down to a case of a single argument. There are [probably] many ways to rewrite the example above to be better optimized, but I explicitly wrote it the most evident way.
The more evident (yet idiomatically wrong) way for those coming from OO background would be to zip
and map
tuples to lists:
data
|> Enum.zip()
|> Enum.map(fn e -> e |> Tuple.to_list() |> Enum.reduce(&Kernel.+/2) end)
defmodule ListSum do
def mapper([inner1 | [inner2 | rest]]) do
reduced = inner1
|> Enum.zip(inner2)
|> Enum.map(fn {v1, v2} -> v1 + v2 end)
mapper([reduced | rest])
end
def mapper([list]) when is_list(list), do: list
def ttler(data) do
data
|> Enum.zip()
|> Enum.map(fn e -> e |> Tuple.to_list() |> Enum.sum() end)
end
end
defmodule ListSumBench do
use Benchfella
@list Enum.to_list(1..1_000)
@lists List.duplicate(@list, 1_000)
bench "mapper" do
ListSum.mapper @lists
end
bench "ttler" do
ListSum.ttler @lists
end
end
Results:
Compiling 2 files (.ex)
Generated ttl app
Settings:
duration: 1.0 s
## ListSumBench
[16:49:06] 1/2: mapper
[16:49:09] 2/2: ttler
Finished in 5.56 seconds
## ListSumBench
benchma iterations average time
mapper 50 50194.78 µs/op
ttler 10 223662.80 µs/op
The difference between Enum.sum
and Enum.reduce(&Kernel.+/2)
is insignificant, but sum
is a bit faster (like 3%.)
Upvotes: 3