patrick_corrigan
patrick_corrigan

Reputation: 899

Zip together sub arrays of a 2 dimensional array

Is there a general way of doing this ?

Each sub array will be of the same length.

c = [[1,1,1,1], [2,2,2,2], [3,3,3,3]]

c[0].zip(c[1], c[2])

=> [[1,2,3][1,2,3],[1,2,3],[1,2,3]]

Thank you.

Upvotes: 1

Views: 691

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110725

Edit: I discovered why my method is so fast, which may have implications that are both good and bad, depending on how the results are to be used. Suppose

c = [[1,1,1],[2,2,2]]

Then

d = [c.map(&:first)]*c.first.size #=> [a, b, c]

where:

a = b = c = [1,2]

but that's because:

a.object_id = b.object_id = c.object.id

So the "bad" is that if an element of d is changed, all the elements in that row are changed to the same value. The "good" is that if the array d will not be changed, not only is this method fast, but it requires very little storage to save the (representation) of the resulting array d.

The truth, however, is that if d is not to be changed, it is pointless to create it. Instead, the code should be refactored so that only the first element of d is used in subsequent operations. (This remark applies to all the methods, of course.)

end of edit

If you intend each element (row) of c to contain elements that are all equal to one another, and they are all the same size, as in your example, you could do this:

[c.map(&:first)]*c.first.size

Out of curiosity I decided to benchmark this method and the two @sawa offered.

Benchmarking code

require 'benchmark'

def sawa_zip(c)       c.first.zip(*c.drop(1))       end
def sawa_transpose(c) c.transpose                   end
def cary(c)           [c.map(&:first)]*c.first.size end

def bench_em(n, m, iterations)
  puts "n = #{n}, m = #{m}, interations = #{iterations}\n" 
  c = n.times.map { Array.new }.map.with_index { |_,i| Array.new(m,i) } 
  Benchmark.bm(%w[sawa_zip, sawa_transpose, cary].map(&:size).max) do |bm|

    bm.report('sawa_zip') do
      iterations.times do
        sawa_zip(c)
      end
    end

    bm.report('sawa_transpose') do
     iterations.times do
       sawa_transpose(c)
     end
    end

    bm.report('cary') do
      iterations.times do
        cary(c)
      end
    end
  end
end

bench_em(200, 300,5)
bench_em(2000, 3000,5)
bench_em(10000, 15000,1)

Benchmark results

It should be kept in mind that this comparison is only valid when all elements in each row of the matrix are equal. I had expected the method I suggested to be relatively fast, but not as fast as indicated by the results.

n = 200, m = 300, interations = 5
                      user     system      total        real
sawa_zip          0.010000   0.000000   0.010000 (  0.007858)
sawa_transpose    0.000000   0.000000   0.000000 (  0.006568)
cary              0.000000   0.000000   0.000000 (  0.000113)

n = 2000, m = 3000, interations = 5
                      user     system      total        real
sawa_zip          1.010000   0.070000   1.080000 (  1.080286)
sawa_transpose    0.800000   0.060000   0.860000 (  0.860823)
cary              0.000000   0.000000   0.000000 (  0.001669)

n = 10000, m = 15000, interations = 1
                      user     system      total        real
sawa_zip         25.760000   0.740000  26.500000 ( 26.668127)
sawa_transpose   18.200000   0.630000  18.830000 ( 18.870150)
cary              0.000000   0.000000   0.000000 (  0.002412)

Upvotes: 3

sawa
sawa

Reputation: 168199

To do it with zip:

c.first.zip(*c.drop(1))

Otherwise,

c.transpose

will be a symmetric way.

Upvotes: 7

Related Questions