Search code examples
arraysrubymatrixsketchup

How to sum 2-dimensional arrays


I have a two-dimensional array with sub-arrays of equal size, for example:

array = [
  [10, 12, 15 ,17], [16, 32, 65, 47], [45, 48, 41, 23],
  [36, 25, 74, 98], [32, 19, 66, 88]
]

I would like to create a new array by summing the corresponding elements of every 4th sub-array, i.e. the elements that are "on top of each other" in the above example:

new_array = [
  [10 + 36, 12 + 25, 15 + 74, 17 + 98],
  [16 + 32, 32 + 19, 65 + 66, 47 + 88],
  [45, 48, 4‌​1, 23]
]

These are just examples, the actual arrays can be larger.


Solution

  • Complete Matrix

    You can use each_slice, transpose, map and transpose again to navigate your matrix. The code first uses join('+') to show what is being calculated :

    array= [[10,12,15,17],[16,32,65,47],[45,48,41,23],[36,25,74,98],[32,19,66,88],[1,2,3,4]]
    
    array.each_slice(3).to_a.transpose.map{|r| r.transpose.map{|x| x.join('+')}}
    # => [["10+36", "12+25", "15+74", "17+98"], ["16+32", "32+19", "65+66", "47+88"], ["45+1", "48+2", "41+3", "23+4"]]
    
    array.each_slice(3).to_a.transpose.map{|r| r.transpose.map{|x| x.inject(:+)}}
    # => [[46, 37, 89, 115], [48, 51, 131, 135], [46, 50, 44, 27]]
    

    Warning!

    You need to carefully select the each_slice parameter to suit your original array. transpose might raise an exception otherwise :

    array = [[10,12,15,17],[19,32,65,47],[45,48,41,23],[36,25,74,98],[10,12,15,17],[16,98,65,47],[69,48,65,23],[66,25,74,98]]
    array.each_slice(3).to_a.transpose.map{|r| r.transpose.map{|x| x.inject(:+)}}
    #=> IndexError: element size differs (2 should be 3)
    array.each_slice(4).to_a.transpose.map{|r| r.transpose.map{|x| x.inject(:+)}}
    #=> [[20, 24, 30, 34], [35, 130, 130, 94], [114, 96, 106, 46], [102, 50, 148, 196]]
    

    Incomplete Matrix

    If the matrix size isn't a multiple of width :

    array = [
      [10, 12, 15 ,17], [16, 32, 65, 47], [45, 48, 41, 23],
      [36, 25, 74, 98], [32, 19, 66, 88]
    ]
    

    you could add subarrays full of 0s to get :

    matrix = [
      [10, 12, 15 ,17], [16, 32, 65, 47], [45, 48, 41, 23],
      [36, 25, 74, 98], [32, 19, 66, 88], [ 0,  0,  0,  0]
    ]
    

    Array#fill does the job :

    def maxtrix_column_sums(array, width)
      size    = array.size
      size2   = array.first.size
      missing = (-size) % width
      matrix  = array.dup.fill(Array.new(size2, 0), size...size + missing)
      matrix.each_slice(width).to_a.transpose.map { |r| r.transpose.map { |x| x.join('+') } }
    end
    
    p maxtrix_column_sums(array, 3)
    #=> [["10+36", "12+25", "15+74", "17+98"], ["16+32", "32+19", "65+66", "47+88"], ["45+0", "48+0", "41+0", "23+0"]]