50

I have a method where I would like to decide what to return within a map function. I am aware that this can be done with assigning a variable, but this is how I though I could do it;

def some_method(array)
    array.map do |x|
      if x > 10
         return x+1 #or whatever
      else
         return x-1
      end
    end
end

This does not work as I expect because the first time return is hit, it returns from the method, and not in the map function, similar to how the return is used in javascript's map function.

Is there a way to achieve my desired syntax? Or do I need to assign this to a variable, and leave it hanging at the end like this:

def some_method(array)
    array.map do |x|
      returnme = x-1
      if x > 10
         returnme = x+1 #or whatever
      end
      returnme
    end
end
Automatico
  • 12,420
  • 9
  • 82
  • 110
  • 1
    Related question with some more answers and details: [Using 'return' in a Ruby block](https://stackoverflow.com/q/2325471). – tanius May 23 '20 at 20:40
  • 1
    `return` in javascript has the opposite behavior. It *does* return from the map function and *not* from any outer method. Sorry, just clarifying cause it sounds like you're saying they behave the same. – papiro Apr 16 '22 at 00:05

3 Answers3

97

Sergio's answer is very good, but it's worth pointing out that there is a keyword that works the way you wanted return to work: next.

array.map do |x|
  if x > 10
    next x + 1
  else
    next x - 1
  end
end

This isn't a very good use of next because, as Sergio pointed out, you don't need anything there. However, you can use next to express it more explicitly:

array.map do |x|
  next x + 1 if x > 10
  x - 1
end
iCodeSometime
  • 1,444
  • 15
  • 30
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • 4
    That is nice! My actual code is a bit more convoluted than my example above, so `next` could be a very nice solution. Personally I don't like all this "magic by default" behaviour of ruby. It is nice to explicitly state what is going on so it is easier for the next person to pick it up. – Automatico Sep 08 '16 at 13:21
  • Cool, I have used Ruby a lot but have missed this with `next`. Some day I will start reading manuals... – 244an Dec 04 '19 at 20:34
31

You don't need the variable. Return value of the block is value of last expression evaluated in it. In this case, the if.

def some_method(array)
    array.map do |x|
      if x > 10
         x+1
      else
         x-1
      end
    end
end

Ternary operator would look nicer, I think. More expression-ish.

def some_method(array)
  array.map do |x|
    (x > 10) ? x+1 : x-1
  end
end

If you insist on using return, then you could use lambdas. In lambdas, return behaves like in normal methods.

def some_method(array)
  logic = ->(x) {
    if x > 10
      return x + 1
    else
      return x - 1
    end
  }
  array.map(&logic)
end

This form is rarely seen, though. If your code is short, it surely can be rewritten as expressions. If your code is long and complicated enough to warrant multiple exit points, then probably you should try simplifying it.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
1

The return keyword can only be used within a method(actually including Proc). It will raise the LocalJumpError

irb(main):123:0> array = [1, 2, 3, 4, 5, 8, 9, 10, 11, 14, 15]
irb(main):124:0> array.map { |n| return n }
Traceback (most recent call last):
        3: from (irb):124
        2: from (irb):124:in `map'
        1: from (irb):124:in `block in irb_binding'
LocalJumpError (unexpected return)

You can use next instead of return.

irb(main):126:0> array.map { |n| next n }
=> [1, 2, 3, 4, 5, 8, 9, 10, 11, 14, 15]
TorvaldsDB
  • 766
  • 9
  • 8