brslv brslv - 1 month ago 7
Ruby Question

Map on collection returns nil

I'm currently learning ruby and playing around with collections. I'm now stuck on a problem and need some help to understand why something works this way.

So, we have a simple array:

arr = ["cat", "dog", "pig", "goat"]


Now, I want to make every second elem uppercased and reversed. The first thing that I thought was the ternary:

modified = arr.each_with_index.map do |el, idx|
((idx + 1) % 2 == 0) ? el.upcase.reverse : el
end


No big deal. But I played around and came up with another solution.

modified = arr.each_with_index.map do |el, idx|
el.upcase.reverse if (idx + 1) % 2 == 0
el unless (idx + 1) % 2 == 0
end


It seems correct to me, but not to the interpreter.

With the ternary I get the result:

["cat", "GOD", "pig", "TAOG"]


But with the later approach:

["cat", nil, "pig", nil]


Can somebody, please, explain why the later doesn't work (it puts nill on every second elem)?

If I do it with regular if-else, it works again.

modified = arr.each_with_index.map do |el, idx|
if (idx + 1) % 2 == 0
el.upcase.reverse
else
el
end
end


How the regular if is different than the one-liners?

Thanks in advance!

Answer

Array#map would use the return value of the block passed in parameter to build the new array.

Since you are not using any return statement, it will always be the last expression evaluated.

In your first and third solutions, you always "hit" el or el.upcase.reverse

In your second solution, when (idx + 1) % 2 == 0, you are not doing anything, so you return simply nil.

Hope this helps.

Note : look at Integer#odd? and Integer#even?

Comments