Advance debugging My inject

Could anyone explain what is hap[pening in my inject specs.

describe ‘Array#my_inject’ do
it ‘calls the block passed to it’ do
expect do |block|
[“test array”].my_inject(:dummy, &block)
end.to yield_control.once
end

it ‘makes the first element the accumulator if no default is given’ do
expect do |block|
[“el1”, “el2”, “el3”].my_inject(&block)
end.to yield_successive_args([“el1”, “el2”], [nil, “el3”])
end

it ‘yields the accumulator and each element to the block’ do
expect do |block|
[1, 2, 3].my_inject(100, &block)
end.to yield_successive_args([100, 1], [nil, 2], [nil, 3])
end

it ‘does NOT call the built in Array#inject method’ do
original_array = [“original array”]
expect(original_array).not_to receive(:inject)
original_array.my_inject {}
end

it ‘is chainable and returns a new array’ do
original_array = [“original array”]
expect(original_array.my_inject {}).not_to eq(original_array)
end
end

I dont understand how [“el1”, “el2”, “el3”].my_inject(&block)
gives the result ([“el1”, “el2”], [nil, “el3”])
Also i dont see a block given here.Then how is it expected to give a result.

TIA
Nathasha

Hi Natasha,

As you correctly point out, there is no block given there. This is because it is simply a test to check your method rather than an actual invocation of my_inject. Similarly, it is not expected to give a result, but simply to test which arguments are passed to the block in your method.

In the specific test you mention, there is not a default accumulator provided, so the accumulator (and the first argument passed to the block) should be the first element of the array. The second element of the array is passed as well since it is the next element in the array. Hence the first arguments passed to the block are [el1, el2].

In the second iteration, the accumulator is nil, since there is no actual block given, and the next element in the array (el3) is passed to the block with it.

So to be clear, this test is checking that the arguments passed to the block in your method are [el1, el2] and [nil, el3]. The test is not checking the result of your method.

Hope this helps!

-Jason

1 Like

Its clear now.Thank you so much.

Hi Jason,

Can you explain why this spec results in two arrays while the latter spec results in three arrays? I’m stuck on this problem and I’m not sure what to do to move on.

it ‘makes the first element the accumulator if no default is given’ do
expect do |block|
[“el1”, “el2”, “el3”].my_inject(&block)
end.to yield_successive_args([“el1”, “el2”], [nil, “el3”])
end

it ‘yields the accumulator and each element to the block’ do
expect do |block|
[1, 2, 3].my_inject(100, &block)
end.to yield_successive_args([100, 1], [nil, 2], [nil, 3])
end

Thank you,

Rikey

Hi Rikey,

The important thing to understand is that these two specs are testing what arguments are passed to the block in my_inject. They are not testing what the result of the method is.

The first spec has two arrays passed to the block because there is no accumulator provided, so the first element in the array is used as the accumulator instead. Thus, there are only two elements left to pass to the block and thus two arrays are passed.

In the second spec an accumulator is provided, resulting in each of the three elements being passed to the block in turn, and thus three arrays.

-Jason

Thank you for your help, Jason. I was able to pass the specs with this code, but I was wondering - is there was a cleaner way to do this?

class Array
  def my_inject(accumulator = self[0], &block)
    i = 0

    while i < length
      if i == 0 and accumulator == self[0]
        accumulator = block.call(accumulator, self[i+1])
        i += 2
      else
        accumulator = block.call(accumulator, self[i])
        i += 1
      end
    end
    accumulator
  end
end

Thank you,

Rikey

Yes…you might run into edge cases where an accumulator is present and it is equal to the first element in the array. However in any solution you will need some kind of iteration and an if/else type of structure to account for cases with or without an accumulator. For now, as long as your code passes the specs and you understand why it passes, that is sufficient.

-Jason