Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature #19830] Allow Array#transpose to take an optional element size argument #8167

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

tomstuart
Copy link
Contributor

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 nils 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 nils:

>> [[1, :a], [2, :b], [3, :c]].transpose(2).then { _2.join }
=> "abc"

>> [].transpose(2).then { _2.join }
=> ""

This PR adds an optional argument to Array#transpose to support the above usage.

Something similar was requested eleven years ago in feature #6852. I believe this PR addresses the problem expressed in that issue without compromising backward compatibility with existing callers of #transpose.

This includes the work of initializing the result array with the correct
number of arrays. We’ll want to do the same work if we receive the
element size as an argument instead of inferring it from the size of the
input’s first element. By breaking this setup code out of the loop we
make it possible to reuse it in both cases.

We still need to reassign `tmp` inside the loop for all subsequent
elements. The `if (i > 0)` condition ensures that we don’t call #to_ary
twice on the first element.
This isn’t used yet, but we’ll use it in the next commit.
@tomstuart tomstuart changed the title Allow Array#transpose to take an optional element size argument [Feature #19830] Allow Array#transpose to take an optional element size argument Aug 3, 2023
The main loop expects `tmp` to have been assigned for the first element,
so we have to make sure that happens even if we didn’t need to use `tmp`
to compute the element size.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
1 participant