Feature #19830
openAllow `Array#transpose` to take an optional size argument
Description
One benefit of supplying an initial value to Enumerable#inject
is that it avoids an annoying edge case when the collection is empty:
>> [1, 2, 3].inject(:+)
=> 6 # good
>> [].inject(:+)
=> nil # bad
>> [].inject(0, :+)
=> 0 # good
A similar edge case exists for Array#transpose
:
>> [[1, :a], [2, :b], [3, :c]].transpose
=> [[1, 2, 3], [:a, :b, :c]] # good
>> [].transpose
=> [] # bad
Although no explicit nil
is produced here, the subtle problem is that the caller may assume that the result array contains arrays, and that assumption leads to nil
s in the empty case:
>> [[1, :a], [2, :b], [3, :c]].transpose.then { _2.join }
=> "abc"
>> [].transpose.then { _2.join }
undefined method `join' for nil:NilClass (NoMethodError)
If we allow Array#transpose
to take an optional argument specifying the size of the result array, we can use this to always return an array of the correct size:
>> [[1, :a], [2, :b], [3, :c]].transpose(2)
=> [[1, 2, 3], [:a, :b, :c]] # good
>> [].transpose(2)
=> [[], []] # good
By avoiding an unexpectedly empty result array, we also avoid unexpected downstream nil
s:
>> [[1, :a], [2, :b], [3, :c]].transpose(2).then { _2.join }
=> "abc"
>> [].transpose(2).then { _2.join }
=> ""
Here is a patch which adds an optional argument to Array#transpose
to support the above usage: https://github.com/ruby/ruby/pull/8167
Something similar was requested eleven years ago in #6852. I believe this feature addresses the problem expressed in that issue without compromising backward compatibility with existing callers of #transpose.