array#zip

Hi --

I'm having a problem trying to wrap the zip method in a function.

If I put the following into irb:

[1,2,3].zip [4,5,6],[7,8,9],[10,11,12],[13,14,15]

I get:

=> [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12, 15]]

Now I'm trying to wrap this into a function, where I can pass in an
array of arrays as a parameter to get the same output. I have tried the
following:

def transpose(matrix)
matrix[0].zip matrix[1..-1]
end

However when I pass the following into the function, I get a totally
different result:

transpose([1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15])
=> [[1, [4, 5, 6]], [2, [7, 8, 9]], [3, [10, 11, 12]]]

If I assign the array of arrays to a variable:

m = [1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]

And then perform the operation manually, I get the correct result:

m[0].zip m[1],m[2],m[3],m[4]
=> [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12, 15]]

I've been mucking around for half a day now, trying the get this damn
thing to output correctly, but I'm not sure what I'm doing wrong.

I would like the output as above. Even better still would be the result
repacked as per the original parameter:

=> [[1,4,7],[10,13,2],[5,8,11],[14,3,6],[9,12,15]]

Can anybody help me?

It's probably a better question for the ruby-talk list (general Ruby
questions), but yes, I can help.

Arrays already have a transpose method:

    irb(main):014:0> m
    => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]
    irb(main):015:0> m.transpose
    => [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12, 15]]

If for some reason you need to roll your own, you'd need to do:

   matrix[0].zip(*matrix[1..-1]) # note the * operator

Without the * you're doing this:

   [1,2,3].zip([[4,5,6], [7,8,9], [10,11,12], [13,14,15]])

i.e., you're mapping 1,2,3 to an array with four elements. You really
want to map it to four arrays; the * operator takes care of
"un-arraying" the big array into a list of smaller arrays.

David

Hi --

unknown wrote:

Hi --
Arrays already have a transpose method:

    irb(main):014:0> m
    => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]
    irb(main):015:0> m.transpose
    => [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12, 15]]

David

Thanks so much David. I tried the transpose method but a different
result, which is why I looked at rolling my own. Now that I've tried it
again it works. I must have passed the array in differently. Now that I
try it works fine.

I was wondering why I hadn't got a reply yet, so next time I'll post to
the Ruby forum.

While I've got you, is there an easy way to repack the result into an
array of arrays that have 3 elements, instead of five?

=> [[1,4,7],[10,13,2],[5,8,11],[14,3,6],[9,12,15]]

In ActiveSupport there's a method called Array#in_groups_of, so you
could do:

   m.transpose.flatten.in_groups_of(3)

Flattening will flatten everything, so if you've got more complex
array nesting, beware (or use the flattenx extension, or wait for Ruby
1.9/2.0).

Thanks again.

P.S. Your routing book looks cool, is there a sample chapter available?

Yes; if you go to http://www.awprofessional.com/title/0321509242
there's a "Excerpts" link under the "More Information" header.

David