How can I transpose different sized ruby arrays?
Asked Answered
S

3

12

I have an array:

arr=[[1,2,3],[4,5],[6]],

I have the following code:

arr.transpose 

but it doesn't work,how to solve it?

I am getting

 [[1,2,3],[4,5],[6]].transpose
IndexError: element size differs (2 should be 3)
    from (irb):13:in `transpose'
    from (irb):13
    from /home/durrant

my solution:

arr.reduce(&:zip).map(&:flatten)

output:

[[1, 4, 6], [2, 5, nil], [3, nil, nil]]
Sibby answered 24/9, 2014 at 12:12 Comment(4)
What is the expected result for the given array?Accipitrine
Look this #21455810 to understand why #transpose didn't work.Karim
The answer, though I can't post as such is that the number of elements have to be the same for this operation.Schluter
So, if you have [[1,2,3],[4,5,0],[6,0,0]].transpose => [[1, 4, 6], [2, 5, 0], [3, 0, 0]] that works because the number of elements in each array is the same.Schluter
P
12

A similar answer was posted (but deleted) an hour earlier:

arr = [[1, 2, 3], [4, 5], [6]]

arr[0].zip(*arr[1..-1])
#=> [[1, 4, 6], [2, 5, nil], [3, nil, nil]]

The above is equivalent to:

[1, 2, 3].zip([4, 5], [6])

This approach assumes that your first sub-array is always the longest. Otherwise the result will be truncated:

arr = [[1, 2], [3, 4, 5], [6]]

arr[0].zip(*arr[1..-1])
#=> [[1, 3, 6], [2, 4, nil]]  missing: [nil, 5, nil]
Propitiate answered 24/9, 2014 at 13:8 Comment(2)
I was looking for a safe transpose method, and used your solution. It only seems to work when the first array is bigger than all the other ones. Using it with arr.reverse returns [[6, 4, 1]] instead of [[6, 4, 1], [nil, 5, 2], [nil, nil, 3]].Pennon
@EricDuminil that's correct, I've update the answer accordingly.Propitiate
C
13

Using zip as in Stefan's answer is the most straightforward, but if you insist on using transpose, then:

l = arr.map(&:length).max
arr.map{|e| e.values_at(0...l)}.transpose
# => [[1, 4, 6], [2, 5, nil], [3, nil, nil]]

Or without using either:

Array.new(arr.map(&:length).max){|i| arr.map{|e| e[i]}}
# => [[1, 4, 6], [2, 5, nil], [3, nil, nil]]
Crotty answered 24/9, 2014 at 13:13 Comment(2)
This is the best solution for a safe-transpose. Using zip only works when the first array is bigger than the others.Pennon
Very nice. I suggest you limit your answer to #2 and merely state that it does not depend on the first subarray being the largest.Institution
P
12

A similar answer was posted (but deleted) an hour earlier:

arr = [[1, 2, 3], [4, 5], [6]]

arr[0].zip(*arr[1..-1])
#=> [[1, 4, 6], [2, 5, nil], [3, nil, nil]]

The above is equivalent to:

[1, 2, 3].zip([4, 5], [6])

This approach assumes that your first sub-array is always the longest. Otherwise the result will be truncated:

arr = [[1, 2], [3, 4, 5], [6]]

arr[0].zip(*arr[1..-1])
#=> [[1, 3, 6], [2, 4, nil]]  missing: [nil, 5, nil]
Propitiate answered 24/9, 2014 at 13:8 Comment(2)
I was looking for a safe transpose method, and used your solution. It only seems to work when the first array is bigger than all the other ones. Using it with arr.reverse returns [[6, 4, 1]] instead of [[6, 4, 1], [nil, 5, 2], [nil, nil, 3]].Pennon
@EricDuminil that's correct, I've update the answer accordingly.Propitiate
N
1

If the length of the subarrays don’t match, an IndexError is raised.

irb(main):002:0> arr=[[1,2,3],[4,5],[6]]
=> [[1, 2, 3], [4, 5], [6]]
irb(main):003:0> arr.transpose
IndexError: element size differs (2 should be 3)
    from (irb):3:in `transpose'
    from (irb):3
    from /Users/liuxingqi/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

should be:

irb(main):004:0> arr=[[1,2,3],[4,5,6]]
=> [[1, 2, 3], [4, 5, 6]]
irb(main):005:0> arr.transpose
=> [[1, 4], [2, 5], [3, 6]]

or

irb(main):006:0> arr=[[1,2],[3,4],[5,6]]
=> [[1, 2], [3, 4], [5, 6]]
irb(main):007:0> arr.transpose
=> [[1, 3, 5], [2, 4, 6]]
Nimbus answered 24/9, 2014 at 12:22 Comment(2)
How did you able to answer while question is closed... Strange!!Karim
Yeah funny, I'm curious too.Schluter

© 2022 - 2024 — McMap. All rights reserved.