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