Ruby syntax: break out from 'each.. do..' block
Asked Answered
E

5

72

I am developing a Ruby on Rails app. My question is more about Ruby syntax.

I have a model class with a class method self.check:

class Cars < ActiveRecord::Base
  ...
  def self.check(name)
     self.all.each do |car|
          #if result is true, break out from the each block, and return the car how to...
          result = SOME_CONDITION_MEET?(car) #not related with database
     end

     puts "outside the each block."
  end
end

I would like to stop/break out from the each block once the result is true (that's break the each block if car.name is the same as the name parameter once) AND return the car which cause the true result. How to break out in Ruby code?

Exclamatory answered 14/12, 2011 at 9:46 Comment(0)
K
131

You can break with the break keyword. For example

[1,2,3].each do |i|
  puts i
  break
end

will output 1. Or if you want to directly return the value, use return.

Since you updated the question, here the code:

class Car < ActiveRecord::Base
  # …

  def self.check(name)
    self.all.each do |car|
      return car if some_condition_met?(car)
    end

    puts "outside the each block."
  end
end

Though you can also use Array#detect or Array#any? for that purpose.

Kory answered 14/12, 2011 at 9:54 Comment(0)
N
15

I provide a bad sample code. I am not directly find or check something from database. I just need a way to break out from the "each" block if some condition meets once and return that 'car' which cause the true result.

Then what you need is:

def check(cars, car_name)
  cars.detect { |car| car.name == car_name }
end

If you wanted just to know if there was any car with that name then you'd use Enumerable#any?. As a rule of thumb, use Enumerable#each only to do side effects, not perform logic.

Nathanialnathaniel answered 14/12, 2011 at 10:6 Comment(0)
P
1

You can use break but what your are trying to do could be done much easier, like this:

def self.check(name)
  return false if self.find_by_name(name).nil?
  return true
end

This uses the database. You are trying to use Ruby at a place the database can deal with it better.

You can also use break conditional:

break if (car.name == name)
Purpura answered 14/12, 2011 at 9:55 Comment(5)
I provide a bad sample code. I am not directly find or check something from database. I just need a way to break out from the "each" block if some condition meets once and return that 'car' which cause the true result.Exclamatory
Well, you can even improve this : def self.check(name); self.where(:name => name).any?; endEmulsify
the first snippet can be written: !!self.find_by_name(name).Nathanialnathaniel
@Nathanialnathaniel => I didnt edit the answer because the thread opener just posted this as an example and the core question is how to break an each not how to check if a dataset with the same name already exists. But anywhere nice code. I never thougt about double negation in coding before^^Purpura
@Mellon: Then you should update the question to reflect what you really want.Nathanialnathaniel
C
1

you can use include? method.

def self.check(name)
  cars.include? name
end

include? returns true if name is present in the cars array else it returns false.

Criner answered 14/12, 2011 at 10:14 Comment(0)
H
1

I had to do this exact same thing and I was drawing a blank. So despite this being a very old question, here's my answer:

Note: This answer assumes you don't want to return the item as it exists within the array, but instead do some processing on the item and return the result of that instead. That's how I originally read the question, I realise now that was incorrect - though this approach can be easily modified for that effect (break item insead of break output)

Since returning from blocks is dodgy (nobody likes it, and I think the rules are about to change which makes it even more fraught) this is a much nicer option:

collection.inject(nil) do |_acc, item|
  output = expensive_operation(item)
  break output if output
end

Note that there are lots of variants; for example, if you don't want an incidental variable, and don't mind starting a second loop in some circumstances, you can invert it like this:

collection.inject(nil) do |acc, item|
  break acc if acc
  expensive_operation(item)
end
Helmer answered 3/12, 2019 at 17:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.