How to reduce two 2 dimensional array to column sums

say we have a simple data array:

[
 [1,2,3,4,5],
 [1,2,3,4,5],
 [1,2,3,4,5],
 [1,2,3,4,5],
]

How can we reduce this to a column sums: [4,8,12,16,20] (ie sum each column).
Also, how can we extend same to an NxM sized array?

The sum of each row is 14 which does not match with your example result, so I’m not sure what you want…

I think he’s summing columns not rows. @CharlesO what have you tried so far? What’s the motivating problem you’re trying to solve?

1 Like

Total row

Adding a total row to report data
Report data comes as NxM array

If that’s true, I’d transpose and then sum each list.

oh that makes sense, totally did not think of that

You keep saying row, but surely you mean column? In what you presented, [1,2,3,4,5] is a row, which sums to 15 every time. If you want to do [1+1+1+1, 2+2+2+2,...] that’s fine, but that’s a column sum not a row.

you are correct, i have updated the question. I need column sums

At least from the most common point of view. Most of us consider matrices as lists of rows. Some might view them as list of columns because it fits better in the data model.

1 Like

there is nothing built in for Transpose in Enum or List

Enum.zip(list) should do it.

Enum.zip gives:[{1, 1, 1, 1}, {2, 2, 2, 2}, {3, 3, 3, 3}, {4, 4, 4, 4}, {5, 5, 5, 5}]

You need to make the tuples lists again after that.

Google’s first hit for transposing a list in Elixir was a thread in this forum:

2 Likes

Using the matrix library tensor which was made for these kinds of tasks:

iex> alias Tensor.Matrix
iex> matrix = Tensor.Matrix.new([
 [1,2,3,4,5],
 [1,2,3,4,5],
 [1,2,3,4,5],
 [1,2,3,4,5],
], 4, 5)
iex> matrix |> Matrix.transpose |> Enum.map(&Enum.sum/1)
[4,8,12,16,20]

And if you want to do other things with these two-dimensional structures as well, I’d suggest turning them into matrix structs as early as possible: A linked-list-of-lists is takes linear time to access elements, and needs special care to keep the invariant of a matrix intact that all rows are the same length.

6 Likes

“Just for giggles”

defmodule Demo do
  def sum_list_columns([]),
    do: nil

  def sum_list_columns([[] | _]),
    do: nil

  def sum_list_columns([[_ | _] | _] = data),
    do: sum_list_columns(data, [], [0])

  defp sum_list_columns([], [[] | _], sums),
    do: :lists.reverse(sums)

  defp sum_list_columns([], [[_ | _] | _] = done_lists, sums),
    do: sum_list_columns(done_lists, [], [0 | sums])

  defp sum_list_columns([[head | tail] | more_lists], done_lists, [sum | rest]),
    do: sum_list_columns(more_lists, [tail | done_lists], [sum + head | rest])
end

data = [
  [1, 2, 3, 4, 5],
  [1, 2, 3, 4, 5],
  [1, 2, 3, 4, 5],
  [1, 2, 3, 4, 5]
]

Demo.sum_list_columns(data)
|> inspect()
|> IO.puts()
$ elixir demo.exs
[4, 8, 12, 16, 20]
$

Or “refactored”:

defmodule Demo do
  def show(value) do
    value
    |> inspect()
    |> IO.puts()
  end

  def reduce_columns([], _, _),
    do: nil

  def reduce_columns([[] | _], _, _),
    do: nil

  def reduce_columns([[_ | _] | _] = data, id, fun),
    do: reduce_columns(data, id, fun, [], [id])

  defp reduce_columns([], _id, _fun, [[] | _], accs),
    do: :lists.reverse(accs)

  defp reduce_columns([], id, fun, [[_ | _] | _] = done_lists, accs),
    do: reduce_columns(:lists.reverse(done_lists), id, fun, [], [id | accs])

  defp reduce_columns([[head | tail] | more_lists], id, fun, done_lists, [acc | rest]),
    do: reduce_columns(more_lists, id, fun, [tail | done_lists], [fun.(head, acc) | rest])
end

data = [
  [1, 2, 3, 4, 5],
  [1, 2, 3, 4, 5],
  [1, 2, 3, 4, 5],
  [1, 2, 3, 4, 5]
]

id_value = 0

data
|> Demo.reduce_columns(id_value, &Kernel.+/2)
|> Demo.show()

id_value = 1

data
|> Demo.reduce_columns(id_value, &Kernel.*/2)
|> Demo.show()
$ elixir demo.exs
[4, 8, 12, 16, 20]
[1, 16, 81, 256, 625]
$
2 Likes