"for" vs "each" in Ruby
Asked Answered
D

10

231

I just had a quick question regarding loops in Ruby. Is there a difference between these two ways of iterating through a collection?

# way 1
@collection.each do |item|
  # do whatever
end

# way 2
for item in @collection
  # do whatever
end

Just wondering if these are exactly the same or if maybe there's a subtle difference (possibly when @collection is nil).

Daytoday answered 20/7, 2010 at 21:28 Comment(0)
F
340

This is the only difference:

each:

irb> [1,2,3].each { |x| }
  => [1, 2, 3]
irb> x
NameError: undefined local variable or method `x' for main:Object
    from (irb):2
    from :0

for:

irb> for x in [1,2,3]; end
  => [1, 2, 3]
irb> x
  => 3

With the for loop, the iterator variable still lives after the block is done. With the each loop, it doesn't, unless it was already defined as a local variable before the loop started.

Other than that, for is just syntax sugar for the each method.

When @collection is nil both loops throw an exception:

Exception: undefined local variable or method `@collection' for main:Object

Forehand answered 20/7, 2010 at 21:35 Comment(4)
is there a good reason why x remains in the for case or is this bad design :P? Seems to me this is rather unintuitive comparing with most other languages.Goddess
@Goddess The reason why x remains in the for scenario is due to the fact that (generally speaking) keywords don't create new scopes. if, unless, begin, for, while, etc. all work with the current scope. #each however accepts a block. Blocks always add their own scope on top of the current scope. Meaning that declaring a new variable in the block (thus a new scope) will not be accessible from outside the block since that additional scope is not available there.Bearish
So does it mean each is a better option since it release the iterator from memory after the loop.Romish
@ChuckZHB: The iterator is released (assuming no other references). It's only the last value it produced that might stick around. Whether that's a good or bad thing depends on what you want (sometimes it's useful to have the final value after the loop).Tactile
S
52

See "The Evils of the For Loop" for a good explanation (there's one small difference considering variable scoping).

Using each is considered more idiomatic use of Ruby.

Strenuous answered 20/7, 2010 at 21:35 Comment(2)
@zachlatta: Thanks for notifying. I'll edit the link to point to a webarchive.org variant of the article!Strenuous
graysoftinc.com/early-steps/the-evils-of-the-for-loop is the new link, now that JEG2's site is back online.Slipnoose
S
36

Your first example,

@collection.each do |item|
  # do whatever
end

is more idiomatic. While Ruby supports looping constructs like for and while, the block syntax is generally preferred.

Another subtle difference is that any variable you declare within a for loop will be available outside the loop, whereas those within an iterator block are effectively private.

Schou answered 20/7, 2010 at 21:31 Comment(1)
while and until do actually have some very concrete uses that cannot be replaced with each such as for example generating unique values or for REPL's.Marilyn
S
7

One more difference:

number = ["one", "two", "three"]
 => ["one", "two", "three"] 

loop1 = []
loop2 = []

number.each do |c|
  loop1 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

for c in number
  loop2 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

loop1[1].call
two
 => nil 

loop2[1].call
three
 => nil 

source: http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-ruby

for more clear: http://www.ruby-forum.com/topic/179264#784884

Secrecy answered 2/8, 2012 at 10:24 Comment(0)
S
7

Never ever use for it may cause almost untraceable bugs.

Don't be fooled, this is not about idiomatic code or style issues. Ruby's implementation of for has a serious flaw and should not be used.

Here is an example where for introduces a bug,

class Library
  def initialize
    @ary = []
  end
  def method_with_block(&block)
    @ary << block
  end
  def method_that_uses_these_blocks
    @ary.map(&:call)
  end
end

lib = Library.new

for n in %w{foo bar quz}
  lib.method_with_block { n }
end

puts lib.method_that_uses_these_blocks

Prints

quz
quz
quz

Using %w{foo bar quz}.each { |n| ... } prints

foo
bar
quz

Why?

In a for loop the variable n is defined once and only and then that one definition is use for all iterations. Hence each blocks refer to the same n which has a value of quz by the time the loop ends. Bug!

In an each loop a fresh variable n is defined for each iteration, for example above the variable n is defined three separate times. Hence each block refer to a separate n with the correct values.

Shafer answered 23/12, 2016 at 22:22 Comment(0)
D
2

It looks like there is no difference, for uses each underneath.

$ irb
>> for x in nil
>> puts x
>> end
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):1
>> nil.each {|x| puts x}
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):4

Like Bayard says, each is more idiomatic. It hides more from you and doesn't require special language features. Per Telemachus's Comment

for .. in .. sets the iterator outside the scope of the loop, so

for a in [1,2]
  puts a
end

leaves a defined after the loop is finished. Where as each doesn't. Which is another reason in favor of using each, because the temp variable lives a shorter period.

Docilu answered 20/7, 2010 at 21:32 Comment(3)
There is a small difference (as yjerem, ChristopheD and Bayard mention) concerning variable scope.Blepharitis
Incorrect, for does not use each underneath. See other answers.Shafer
@Shafer For further clarification please see this question and both its excellent answers.Harber
P
1
(1..4).each { |i| 


  a = 9 if i==3

  puts a 


}
#nil
#nil
#9
#nil

for i in 1..4

  a = 9 if i==3

  puts a

end
#nil
#nil
#9
#9

In 'for' loop, local variable is still lives after each loop. In 'each' loop, local variable refreshes after each loop.

Pammie answered 16/4, 2019 at 2:49 Comment(0)
A
0

As far as I know, using blocks instead of in-language control structures is more idiomatic.

Albright answered 20/7, 2010 at 21:31 Comment(0)
G
0

I just want to make a specific point about the for in loop in Ruby. It might seem like a construct similar to other languages, but in fact it is an expression like every other looping construct in Ruby. In fact, the for in works with Enumerable objects just as the each iterator.

The collection passed to for in can be any object that has an each iterator method. Arrays and hashes define the each method, and many other Ruby objects do, too. The for/in loop calls the each method of the specified object. As that iterator yields values, the for loop assigns each value (or each set of values) to the specified variable (or variables) and then executes the code in body.

This is a silly example, but illustrates the point that the for in loop works with ANY object that has an each method, just like how the each iterator does:

class Apple
  TYPES = %w(red green yellow)
  def each
    yield TYPES.pop until TYPES.empty?
  end
end

a = Apple.new
for i in a do
  puts i
end
yellow
green
red
=> nil

And now the each iterator:

a = Apple.new
a.each do |i|
  puts i
end
yellow
green
red
=> nil

As you can see, both are responding to the each method which yields values back to the block. As everyone here stated, it is definitely preferable to use the each iterator over the for in loop. I just wanted to drive home the point that there is nothing magical about the for in loop. It is an expression that invokes the each method of a collection and then passes it to its block of code. Hence, it is a very rare case you would need to use for in. Use the each iterator almost always (with the added benefit of block scope).

Goaltender answered 12/3, 2019 at 15:33 Comment(0)
O
0

This is very important when using Ruby's async.

require 'async'

Async do
  for i in 5.times do
    Async do
      sleep(1)
      print(i.to_s + " ")
    end
  end
end

Will return: 4 4 4 4 4 as i is not re-defined for every iteration.

require 'async'

Async do
  5.times.each do |i|
    Async do
      sleep(1)
      print(i.to_s + " ")
    end
  end
end

Will return: 0 1 2 3 4 as i only ever lives inside the scope of its iteration's block.

The reason for this becomes fairly obvious when reading the other answers, but since online searches for this kind of problem with async in the query don't return any results mentioning this I left this answer here.

Do not use for .. in .. in conjunction with async!

Outport answered 29/12, 2023 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.