I am new to ruby and I was trying to iterate over a 2d array. I made a mistake in my code. From that mistake, I noticed some unexpected output.
s = [["ham", "swiss"], ["turkey", "cheddar"], ["roast beef", "gruyere"]]
i = 0;
s.each{
|array| so = array[i] # pin
puts so[i]
}
Due to #pin, if i = 0, output is h t r. i = 1 gives w h r. i > 1 gives an error:
C:/Ruby.rb in `block in <main>': undefined method `[]' for nil:NilClass (NoMethodError)
from C:/Ruby.rb:3:in `each'
from C:/Ruby.rb:3:in `<main>'
If I use |array| so = array # pin, then my code does not produce strange output. I'll just fix the remaining stuff to make my code iterate for all values that 'i' can have.
Please explain this.
PS: Working code is here
s = [["ham", "swiss"], ["turkey", "cheddar"], ["roast beef", "gruyere"]]
s.each{
|array| so = array
array.each{
|str| puts str
}
}
For each type of sandwich, when i is 0, so is the 1st element, which is the meat. so[0] is the first letter of the meat.
When i is 1, which is the 2nd element, which is the cheese. so[1] is the second letter of the cheese.
When i is 3, there is no third component to the sandwich. so so is nil. so[2] is asking for the nil[2].
nil is a class, like everything in ruby. But nil does not implement the [] method, as arrays and other classes that implement the Enumerable module do.
Since nil does not support the [] method, then you get the undefined method error.
Even operations that are built into other languages, like +, [], and == are methods that can be overridden in Ruby.
To understand exactly what's happening, try this bit of code:
class NilClass
def [] (i)
nil
end
end
Executing that will open up the existing NilClass, and add a method called []. Then you can do nil[1] and it will return nil. Changing an existing class like this is known as monkey patching in the Ruby world.
When you ask for so[2] you are actually asking for the third element, and if it doesn't exist, you'll get an error.
I recommend structuring your blocks like so:
s.each do |pair|
puts pair
end
Note the do/end instead of {} and the placement of the iteration variable inline with the each. Also note that this is equivalent to your "working code" so you don't need the extra iterator in this case.
Related
What i'm trying to do is create a method that can be given an array as an argument. The array should have some numbers in it. The method will return the number of times the array includes each number inside of it. I understand that there are probably many ways to do this, but I'd appreciate it if folks could help me understand why my way is not working rather than just advising me to do something completely different.
So I start by trying this method out
def score (dice)
dice.each do |die|
x = /(die)/.match(dice.to_s).length
end
x
end
and calling it with score ([5])expecting to get an output of 1. However, I get
NoMethodError: undefined method `length' for nil:NilClass
from t2.rb:22:in `block in score'
from t2.rb:21:in `each'
from t2.rb:21:in `score'
from (irb):2
from /home/macs/.rvm/rubies/ruby-2.0.0-p247/bin/irb:13:in `<main>'
I have also tried changing the match statement slightly (getting rid of the to_s) so it is
def score (dice)
dice.each do |die|
x = /(die)/.match(dice).length
end
x
end
and calling it with score ([5]) I get
TypeError: no implicit conversion of Array into String
from t2.rb:22:in `match'
from t2.rb:22:in `block in score'
from t2.rb:21:in `each'
from t2.rb:21:in `score'
from (irb):2
from /home/macs/.rvm/rubies/ruby-2.0.0-p247/bin/irb:13:in `<main>'
Really not sure how I'm supposed to accomplish this matching.
In this line
/(die)/.match(dice.to_s).length
the method match returns nil if the argument you are passing doesn't match the regular expression, which leads to this error
nil.length
# => NoMethodError: undefined method `length' for nil:NilClass
The method will return the number of times the array includes each
number inside of it.
You can try this
a = [1,1,1,2,2,1,3]
a.uniq.map { |x| a.count(x) }
# => [4, 2, 1]
a.uniq.map { |x| {x => a.count(x)} }
# => [{1=>4}, {2=>2}, {3=>1}]
If you want to count the occurence of each elements in the array, then you can do something like this
def score (dice)
count_hash = {}
dice.uniq.each do |die|
count_hash[die] = dice.count(die)
end
count_hash
end
I'd appreciate it if folks could help me understand why my way is not working ...
/(die)/ creates a Regexp, a pattern that can be matched against a string. Your pattern matches and captures die.
Regexp#match returns a MatchData object if there was a match:
/(die)/.match('a string with die') #=> #<MatchData "die" 1:"die">
# here's the match: ^^^
or nil if there was no match:
/(die)/.match('a string with dice') #=> nil
You are not working with string but with an array of integers. You convert this array to a string using Array#to_s:
dice = [5]
dice.to_s #=> "[5]"
This string doesn't contain die and therefore match returns nil:
/(die)/.match("[5]") #=> nil
Calling nil.length then raises the NoMethodError.
Passing the array "as-is" doesn't work either, because match expects a string:
/(die)/.match([5]) #=> TypeError: no implicit conversion of Array into String
Using a Regexp is not going to work here, you'll have to approach this problem in another way.
This is probably the most rubyish way to solve the problem:
a = [1,1,1,2,2,1,3]
p Hash[a.group_by{|x|x}.map{|key, val| [key,val.size]}]
#=> {1=>4, 2=>2, 3=>1}
An example that might help you implement your logic
a = [2,3,2,8,3]
a.uniq.each {|i| print i, "=>", a.to_s.scan(/#{i}/).length, " times \n" } #=> this works but ugly.
a.uniq.each {|i| print i, "=>", a.count(i), " times \n" } #=> borrowed from one of the answers.
2=>2 times
3=>2 times
8=>1 times
You're getting errors because in both cases you are trying to match a string (5) with the wrong thing.
This tries to match die with the entire array dice converted to a string:
dice.each do |die|
x = /(die)/.match(dice.to_s).length
end
This tries to match die with the dice array itself:
dice.each do |die|
x = /(die)/.match(dice).length
end
Getting started with Rails and I'm stuck with a block operation. I want to loop through a model and update integer values for a new field by calculating difference between two datetime fields. Doing this in the console:
MyModel.all.each do |m|
m.new_integer_field = m.existing_datetime_field - m.parent_object.existing_datetime_field
m.save!
end
The result is
NoMethodError: undefined method `-' for nil:NilClass
It works on one record if I do:
m = MyModel.find(1)
m.new_integer_field = m.existing_datetime_field - m.parent_object.existing_datetime_field
m.save
I guess it's a basic syntax thing, but couldn't find obvious explanations. Grateful to get some pointers forward.
The m.existing_datetime_field in one of the instances was nil and therefore the operation didn't work. You need to check for nil values if not sure:
MyModel.all.each do |m|
unless m.existing_datetime_field == nil
m.new_integer_field = m.existing_datetime_field - m.parent_object.existing_datetime_field
m.save!
end
As I already said probably one of the attributes of the instances are nil.
To check this use #inspect on the instances:
MyModel.all.each {|i| puts i.existing_datetime_field.inspect }
It seems one of these objects is nil and then the - method is not defined.
As you suggested you can check the operands before to calculate the differences.
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).
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
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.
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.
One more different..
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
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.
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.
(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.
As far as I know, using blocks instead of in-language control structures is more idiomatic.
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).
I have code:
class Scene
def initialize(number)
#number = number
end
attr_reader :number
end
scenes = [Scene.new("one"), Scene.new("one"), Scene.new("two"), Scene.new("one")]
groups = scenes.inject({}) do |new_hash, scene|
new_hash[scene.number] = [] if new_hash[scene.number].nil?
new_hash[scene.number] << scene
end
When I'm lauching it I get error:
freq.rb:11:in `[]': can't convert String into Integer (TypeError)
from freq.rb:11:in `block in <main>'
from freq.rb:10:in `each'
from freq.rb:10:in `inject'
from freq.rb:10:in `<main>'
If I change scenes to:
scenes = [Scene.new(1), Scene.new(1), Scene.new(2), Scene.new(1)]
the problem dissapear.
Why I get error message in the first case? Why Ruby decide to convert scene.number from String to Integer?
And one additional question about the 'inject' method. When Ruby initialize the 'new_hash' variable and how can Ruby know the type of this variable?
try:
groups = scenes.inject({}) do |new_hash, scene|
new_hash[scene.number] = [] if new_hash[scene.number].nil?
new_hash[scene.number] << scene
new_hash
end
Ruby takes the empty hash passed into inject() and sets new_hash to that. When the block ends the return value gets used to initialize new_hash the next time through, i.e., new_hash keeps accumulating the result of the block.
In your original code you were not returning the hash but an array (new_hash[scene.number] is an array) and the next loop through Ruby complained because new_hash[scene.number] was trying to do a lookup into the array with a string value, hence the error you got.
Z.E.D.'s right. See Jay Fields' Thoughts: Ruby: inject for a good explanation of inject by example.
As presented, your block returns an array. So the new_hash in |new_hash, scene| ends up being that array. When Ruby tries to find the array index 'one', it throws the error because 'one' is a String, not an Integer.
All you need to do is return new_hash as Z.E.D. showed, and you'll get something like this:
{
"two" => [
#<Scene:0x101836470 #number="two">
],
"one" => [
#<Scene:0x101836510 #number="one">,
#<Scene:0x1018364c0 #number="one">,
#<Scene:0x101836420 #number="one">
]
}
Why not use group_by which is probably exactly what you try to accomblish?
groups = scenes.group_by(&:number)
# => {"two"=>[#<Scene:0xb728ade0 #number="two">],
# "one"=>
# [#<Scene:0xb728ae30 #number="one">,
# #<Scene:0xb728ae08 #number="one">,
# #<Scene:0xb728ada4 #number="one">]}
inject is a folding operation and not exactly what you want. At least it's cumbersome to use in this way. merge with a block would probably be appropriate if you want to apply some algorithm during merging or grouping.
Also, to explain 'how can Ruby know the type of this variable' and why it tries to 'convert String into Integer' you might want to revise: Ruby variables and dynamic typing.
I know an answer is accepted for this question, but I can't help but post my answer.
groups = scenes.inject({}) { |nh, s| nh.tap {|h| (h[s.number] ||= []) << s } }
I'm new to Ruby, and I'm trying the following:
mySet = numOfCuts.times.map{ rand(seqLength) }
but I get the 'yield called out of block' error. I'm not sure what his means. BTW, this question is part of a more general question I asked here.
The problem is that the times method expects to get a block that it will yield control to. However you haven't passed a block to it. There are two ways to solve this. The first is to not use times:
mySet = (1..numOfCuts).map{ rand(seqLength) }
or else pass a block to it:
mySet = []
numOfCuts.times {mySet.push( rand(seqLength) )}
if "numOfCuts" is an integer,
5.times.foo
is invalid
"times" expects a block.
5.times{ code here }
You're combining functions that don't seem to make sense -- if numOfCuts is an integer, then just using times and a block will run the block that many times (though it only returns the original integer:
irb(main):089:0> 2.times {|x| puts x}
0
1
2
map is a function that works on ranges and arrays and returns an array:
irb(main):092:0> (1..3).map { |x| puts x; x+1 }
1
2
3
[2, 3, 4]
I'm not sure what you're trying to achieve with the code - what are you trying to do? (as opposed to asking specifically about what appears to be invalid syntax)
Bingo, I just found out what this is. Its a JRuby bug.
Under MRI
>> 3.times.map
=> [0, 1, 2]
>>
Under JRuby
irb(main):001:0> 3.times.map
LocalJumpError: yield called out of block
from (irb):2:in `times'
from (irb):2:in `signal_status'
irb(main):002:0>
Now, I don't know if MRI (the standard Ruby implementation) is doing the right thing here. It probably should complain that this does not make sense, but when n.times is called in MRI it returns an Enumerator, whereas Jruby complains that it needs a block.
Integer.times expects a block. The error message means the yield statement inside the times method can not be called because you did not give it a block.
As for your code, I think what you are looking for is a range:
(1..5).map{ do something }
Here is thy rubydoc for the Integer.times and Range.