Repacking an array of arrays


#1

Is there an elegant (maybe a one-liner) for repacking:

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

to?

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

So rather than having 3 arrays each with 5 elements, I get five arrays
of 3 elements each…

the only way I can see to do this is by looping the array, and creating
new arrays.

Any help would be much appreciated.


#2

Hi –

On Tue, 10 Jul 2007, Kaps L. wrote:

the only way I can see to do this is by looping the array, and creating
new arrays.

If you have ActiveSupport you can do:

array.flatten.in_groups_of(3)

with the caveat about flatten that it flattens greedily. If you
don’t, you can easily get it or roll your own in_groups_of. (It was
one of the first things I ever wrote for myself in Ruby, though I
think I called it in_slices_of or something.)

David


#3

unknown wrote:

If you have ActiveSupport you can do:

array.flatten.in_groups_of(3)

David

Thanks again David.


#4

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

a0 = a.shift

p a0.zip(*a)

to grok this, ri Array#zip


#5

2007/7/10, Kaps L. removed_email_address@domain.invalid:

the only way I can see to do this is by looping the array, and creating
new arrays.

Try this:

require ‘enumerator’
p [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12,
15]].flatten.to_enum(:each_slice, 3).to_a

Kind regards

robert


#6

2007/7/10, SonOfLilit removed_email_address@domain.invalid:

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

a0 = a.shift

p a0.zip(*a)

This outputs

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

Which is not what the OP wanted according to his first posting (see
quote below). Still it’s a nice approach!

So rather than having 3 arrays each with 5 elements, I get five arrays
of 3 elements each…

the only way I can see to do this is by looping the array, and creating
new arrays.

Any help would be much appreciated.

Kind regards

robert


#7

Hi –

On Tue, 10 Jul 2007, Robert K. wrote:

of 3 elements each…

the only way I can see to do this is by looping the array, and creating
new arrays.

Try this:

require ‘enumerator’
p [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12,
15]].flatten.to_enum(:each_slice, 3).to_a

And in 1.9 I guess it would be:

array.flatten.each_slice(3).to_a

I’d like to see a method (I don’t think there is one) in 1.9 that
would return the sliced-up array without the need for the explict to_a
operation. I suspect the most common use will be exactly that.

David


#8

Oi…

How could I have misread so badly?

I’m sorry, I had this so cool elegant hammer and I guess I immediately
saw a nail :stuck_out_tongue:

Aur


#9

Robert K. wrote:

2007/7/10, Kaps L. removed_email_address@domain.invalid:

the only way I can see to do this is by looping the array, and creating
new arrays.

Try this:

require ‘enumerator’
p [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12,
15]].flatten.to_enum(:each_slice, 3).to_a

Kind regards

robert

Thanks Robert,

That’s an interesting solution using the enumerator. I’ll have to add
that to the memory banks. I ended up using David’s solution from
ActiveSupport. It’s very elegant.


#10

On Jul 10, 2007, at 7:26 AM, removed_email_address@domain.invalid wrote:

arrays

And in 1.9 I guess it would be:

array.flatten.each_slice(3).to_a

I’d like to see a method (I don’t think there is one) in 1.9 that
would return the sliced-up array without the need for the explict to_a
operation. I suspect the most common use will be exactly that.

I bet rolling …_with_index iterators will be pretty common too:

enum.each_with_index.inject…

James Edward G. II


#11

On Jul 9, 10:00 pm, Kaps L. removed_email_address@domain.invalid wrote:

the only way I can see to do this is by looping the array, and creating
new arrays.

Any help would be much appreciated.

Is the fact that it goes from 3 of 5 to 5 of 3 significant or do you
always want to go to some number of 3 element arrays? E.g. would you
ever have an array of 4 arrays of 6 elements and want to go to an
array of 6 arrays with 4 elements each?

If it’s the first case, this will work, though there may be something
more elegant:

result = []
a.flatten.each_with_index {|x, i| i % 3 == 0 ? result << [x] :
result[-1] << x}

I suppose, if you need the second case, you could do something a
little more general:

result = []
a.flatten.each_with_index {|x, i| i % a.length == 0 ? result << [x] :
result[-1] << x}

Jeremy


#12

On Tue, 10 Jul 2007 12:00:47 +0900, Kaps L. wrote:

the only way I can see to do this is by looping the array, and creating
new arrays.

Any help would be much appreciated.

require ‘enumerator’
a=[[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12, 15]]
a.flatten.enum_slice(3).collect
=> [[1, 4, 7], [10, 13, 2], [5, 8, 11], [14, 3, 6], [9, 12, 15]]


#13

On Tue, 10 Jul 2007 17:57:40 +0900, SonOfLilit wrote:

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

a0 = a.shift

p a0.zip(*a)

In that case, you’d just use a.transpose