-1

I am trying to do some calculation like the following. option is an empty hash and aes is an array.

options = {}
aes = [1,2]
test = aes.inject([]) do |array, value|
  array << value + 2
  array << value -1 if options[:calculation]  # here options[:calculation] will be nil 
end

I debugged and found that the value of test is nil. The output of array << value -1 if options[:calcuation] is nil, and I want to return the calculated value in test. For resolving this issue, I am using the code below, where aes is an array:

options = {}
aes = [1,2]
test = aes.inject([]) do |array, value|
  array << value + 2
  array << value -1 if options[:calculation]  # here options[:calculation] will be nil 
  array # I am using `array` here
end

Is this correct, or is there any alternate way to do this?

Stefan
  • 96,300
  • 10
  • 122
  • 186
Arvind Kumar
  • 217
  • 2
  • 11

3 Answers3

2

Use Enumerator#with_object instead:

options = {}
test = aes.each.with_object([]) do |value, array|
  array << value + 2
  array << value -1 if options[:calcuation] 
end
ndnenkov
  • 33,260
  • 9
  • 67
  • 97
  • NoMethodError: undefined method `with_object' for # – Arvind Kumar Dec 22 '15 at 07:48
  • @ArvindKumar, which version of Ruby are you using? – ndnenkov Dec 22 '15 at 07:50
  • ruby version is 1.8.7 – Arvind Kumar Dec 22 '15 at 07:51
  • 3
    @ArvindKumar, why are you in the stone ages? Is this a super legacy codebase or are you just using the default version of your distribution? You can try to replace `each.with_object` with `each_with_object` as I don't remember which version introduced it, but don't get your hopes too high. – ndnenkov Dec 22 '15 at 07:53
  • 2
    As @ndn pointed out, 1.8.7 is officially [unsupported](https://www.ruby-lang.org/en/news/2013/06/30/we-retire-1-8-7/) since mid 2013. If at all possible, take the bull by the horns and upgrade as soon as possible. – Drenmi Dec 22 '15 at 08:01
  • Both, `Enumerable#each_with_object` and `Enumerator#with_object` were introduced in Ruby 1.9 – Stefan Dec 22 '15 at 09:45
  • @Stefan, didn't know that. I have a vague memory that `each_with_object` was getting deprecated in favor of `each.with_object` so I assumed one to be older than the other. Will remove it from my answer. – ndnenkov Dec 22 '15 at 09:48
1

In Ruby, result of last statement executed in a method is the return value. If that last statement happens to be a conditional statement and does not execute, then return value is nil.

def meth
    var = nil
    10 if var # will not execute
end
p meth
#=> nil

Same is true for blocks as well.

In case of Array#inject, the return value of block given to inject becomes the new value of accumulator.

Since, in your case, the last statement of block does not execute due to condition being false (as options[:calculation] is nil), the accumulator value inadvertently becomes nil. To avoid it returning nil, you had to return the value of accumulator explicitly.

You could modify your code to something like below if you don't like to use array as explicit return statement.

aes = [1,2,3,4,3,4]
options = {}
test = aes.inject([]) do |array, value|
  array + [value + 2] + (options[:calculation] ? [value - 1] : [])
end
#=> [3, 4, 5, 6, 5, 6]
Wand Maker
  • 17,377
  • 6
  • 43
  • 79
0

You are somehow misusing inject. It is supposed to apply "a binary operation" to the elements, not to populate an array.

So, instead of writing:

aes = [1,2]
test = aes.inject([]) do |array, value|
  array << value + 2
  array << value - 1 if options[:calculation]
  array
end

I would simply write:

aes = [1,2]
test = []
aes.each do |value|
  test << value + 2
  test << value - 1 if options[:calculation]
end
Stefan
  • 96,300
  • 10
  • 122
  • 186