Ruby arrays have this handy method called transpose
which takes an existing 2-dimensional array (i.e. a matrix) and flips it on its side:
>> a = [[1,2], [3,4], [5,6]] >> puts a.transpose.inspect [[1, 3, 5], [2, 4, 6]]
Each row becomes a column, essentially. This is fine and dandy for polite arrays. If one of the rows in the original array is not as long as the others, though, Ruby chunders thusly:
>> a = [[1,2], [3,4], [5]] >> a.transpose IndexError: element size differs (1 should be 2) from (irb):3:in `transpose' from (irb):3
That ain’t pretty, especially if the intent behind using
Here’s how to do just that:
class Array def safe_transpose result = [] max_size = self.max { |a,b| a.size <=> b.size }.size max_size.times do |i| result[i] = Array.new(self.first.size) self.each_with_index { |r,j| result[i][j] = r[i] } end result end end
Now we call safe_transpose
on our matrix of courses and Ruby does the right thing. It calculates the length of the longest row and uses that as the baseline to perform the transposition. So our original example becomes:
>> a = [[1,2], [3,4], [5]] >> puts a.transpose.inspect [[1, 3, 5], [2, 4, nil]]
Nice and neat. Caveats: the code above hasn’t been refactored or tested. Your mileage may vary. If you see a better way to do this, let me know and I’ll post an update.
Pingback: Ennuyer.net » Blog Archive » I am way behind on my rails link blogging. Link dump and reboot.
Hey, here’s my idea for a solution:
class Array
def safe_transpose
max_size = self.map(&:size).max
self.dup.map{|r| r << nil while r.size < max_size; r}.transpose
end
end
This seems like a reasonable addition to the Array class in ruby?
Thanks for the solution, Nathan. I like it.
Phil, I agree that it would be a useful addition to Ruby. If not on the Array class itself, at least in a core library somewhere.
You could possibly use #zip? http://www.ruby-doc.org/core-2.2.0/Array.html#method-i-zip