Stanislav Beremski
Stanislav Beremski

Reputation: 102

What is clearest way to add up all elements in 2 dimensional array by position using Ruby?

What is the clearest and most efficient way to add all the elements in 2D array by their position in Ruby. Example:

2darray = [[1,2,3],[1,2,3]]
result = [2,4,6]

I have the following code

def sum_elements_by_position(array)
 total_elements = array.length
 result = []

 for i in 0...array.first.length
   n = 0
   array.each { |subarray| n += subarray[i] }
   result << n
 end
 result
end

Assumptions: All primary elements are of the same length

For bonus points it would be great to see a solution that works primary elements of an arbitrary length

Upvotes: 1

Views: 117

Answers (3)

glenn mcdonald
glenn mcdonald

Reputation: 15478

I'd do this:

a.transpose.map {|x| x.reduce(:+)}

Clean, simple, flexible. The .transpose turns this

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

into this

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

and then .map applies .reduce to each subarray. And .reduce, in turn, aggregates the subvalues by adding them. Or, more precisely, by applying the + method to them.

I highly recommend reading the doc for these functions until you fully understand this example, as it's a pretty good succinct demonstration of how to think in a Rubyish way!

Upvotes: 0

dbenhur
dbenhur

Reputation: 20408

Here's a solution addressing when the rows aren't the same length.

def sum_cols arr
  arr.reduce( [] ) do |res,row| 
    row.each_with_index { |e,i| res[i] ||= 0; res[i] += e }
    res
  end
end

irb> sum_cols [ [0,1,2], [3,4], [5,6,7,8] ]
=> [8, 11, 9, 8]

@oldergod suggested using zip based on the longest row, but finding the longest row and rejecting nils has a cost. I benchmarked the following against the above method using the example array above and found the reduce+each_with_index method more than 30% faster:

def sum_cols_using_zip arr
  max_len = arr.map(&:size).max
  ([0] * max_len).zip(*arr).map do |col|
    col.compact.inject(:+)
  end
end

Upvotes: 0

Ry-
Ry-

Reputation: 224877

You can zip the first row with the rest of them and then do the sum:

def sum_elements_by_position(array)
    array[0].zip(*array[1..-1]).map do |col|
        col.inject(:+)
    end
end

Upvotes: 3

Related Questions