简体   繁体   中英

Why Ruby .map doesn't work with if statement

I'm getting an unexpected result with using if inside .map :

a = [1,2,3,4,5]
a.map do |item|
  item + 10 if item < 4
  item + 9  if item > 4
end

I expected: [11,12,13,13,14]
...but I'm instead getting: [nil, nil, nil, nil, 14]

Why's that?

Secondly, I know that the last expression is the return value of method So does this true with if statement? The last expression is the return value of if statement.

Thanks!

For elements that are < 4, item + 10 if item < 4 does indeed return the expected value. But then the second statement executes, and map returns that value. For item < 4, item + 9 if item > 4 returns nil .

Your map should look like this instead:

a.map do |item|
  if item < 4 then
      item + 10
  else
      item + 9
  end
end

What do you do if item == 4?

We can demonstrate the problem you have by writing the block as a method:

def test_map(item)
  item + 10 if item < 4
  item + 9  if item > 4
end

test_map 3
# => nil
test_map 5
# => 14

What happens here? For item=3 the first line returns 13 , but that's not what's returned from the method - the method continues to the next line, which is evaluated to nil ...

In order to return a single value according to several conditions, you can use if..elsif..else construct, or a case..when construct:

def test_map2(item)
  case item
    when 0..4
      item + 10
    when 4..10
      item + 9
    else
      item
  end
end

test_map2 3
# => 13
test_map2 5
# => 14

case..when returns the block after the first when clause which is evaluated to true.

It's because if you use map , you create array containing values evaluated from block passed into map method. So in this case, the last value evaluated in first 4 elements is item + 9 if item > 4 , which returns nil .

Ruby 2.7+

There be a solution now!

Ruby 2.7 is introducing filter_map for this exact purpose. It's idiomatic and performant, and I'd expect it to become the norm very soon.

For example:

numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]

Here's a good read on the subject .

Hope that's useful to someone!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM