Reputation: 11
I need to use a for
loop to take the last four strings off the more_stuff
array.
Here is my code:
# assigns string Apples Oranges Crows Telephone Light Sugar to variable
ten_things
ten_things = "Apples Oranges Crows Telephone Light Sugar"
# prints Wait there are not ten things on that list. Let's fix that.
puts "Wait there are not 10 things in that list. Let's fix that."
# first seperates string ten_things into an array at space character, then
# assigns the new array to variable stuff
stuff = ten_things.split(' ')
# assigns array to more stuff with strings, Day, Night, Song, Frisbee, Girl,
# and Boy
more_stuff = ["Day", "Night", "Song", "Frisbee", "Corn", "Banana", "Girl",
"Boy"]
# using math to make sure there's 10 items
# assigns fifth through eighth elements in array more_stuff to array stuff_3
stuff_3 = more_stuff[4..8]
# prints array stuff_3 in brackets
puts "#{stuff_3}"
# for all strings in stuff_3
stuff_3.each do |stuff_3|
# pops the last item off of stuff_3
next_one = stuff_3.pop
# puts adding (next_one)
puts "Adding #{next_one}"
# adds (next_one) to array stuff
stuff.push(next_one)
# ends for loop
end
Also here is the error that comes up when I run it from Powershell:
Wait there are not 10 things in that list. Let's fix that.
["Corn", "Banana", "Girl", "Boy"]
ex38for.rb:17:in `block in <main>': undefined method `pop' for "Corn":String
(NoMethodError)
from ex38for.rb:16:in `each'
from ex38for.rb:16:in `<main>'
I'm confused how for
loops work, specifically each
and where to put stuff in the array command.
Upvotes: 1
Views: 1449
Reputation: 33420
What pop
does is to take the last element or value on an array (when is used without specifying the number of elements):
p stuff_3.pop
# => "Boy"
p stuff_3.pop(2)
# => ["Banana", "Girl"]
But in your case you're trying to use pop
with an element that's inside the main array.
If you check it doing it outside your each
method:
puts stuff_3.pop
# => Boy
Then that will print Boy
because is the last element within the stuff_3
array which you declare as more_stuff[4..8]
:
stuff_3 = more_stuff[4..8]
p stuff_3
# => ["Corn", "Banana", "Girl", "Boy"]
But then, when you do stuff_3.each do |stuff_3|
you're using the same name stuff_3
to access each element inside that array, which has the same name. So there's where you're getting the undefined method 'pop' for "Corn":String
error, because pop
is waiting for an array
and if you iterate over each element inside stuff_3
you're getting String
elements.
A possible solution is that you use a different name to access the elements when you use each
with the stuff_3
array, maybe just as stuffs
, and that will give you "Adding Boy" and "Adding Girl".
Or maybe any word to refer the elements inside stuff_3
being different to this one could work, because you're not accessing those elements, you also could use _
to specify they're not being used:
stuff_3.each do |_|
next_one = stuff_3.pop
puts "Adding #{next_one}"
stuff.push(next_one)
end
This would be stuff
before pushing the elements:
["Apples", "Oranges", "Crows", "Telephone", "Light", "Sugar"]
And after:
["Apples", "Oranges", "Crows", "Telephone", "Light", "Sugar", "Boy", "Girl"]
Upvotes: 3
Reputation: 5345
Let's focus on this part of your code:
stuff_3.each do |stuff_3|
# pops the last item off of stuff_3
next_one = stuff_3.pop
# puts adding (next_one)
puts "Adding #{next_one}"
# adds (next_one) to array stuff
stuff.push(next_one)
# ends for loop
end
This code: stuff_3.each do |stuff_3|
is a loop which takes each item in stuff_3
, saves it to a local variable (which you have also called stuff_3
) and executes the following code with it.
So when you call stuff_3.pop
, you're actually calling pop
on the local variable which contains only one item, instead of the array containing all the items. Because of this, you get the error:
undefined method `pop' for "Corn":String
Which is telling you that you're calling pop
on a single item (the String "Corn" in this case), instead of an array of items like you expected.
One way to fix this is to change your code like so:
stuff_3.each do |next_one|
# puts adding (next_one)
puts "Adding #{ next_one }"
# adds (next_one) to array of stuff
stuff.push(next_one)
# ends for loop
end
This code will go through each item in stuff_3
and add it to stuff
. But it may work differently than you expect:
stuff_3.each
will iterate through items from first to last, whereas pop
takes the last item first, meaning that this code will add items to your array in opposite order.stuff_3.each
goes through items without removing them, whereas pop
will remove each item, eventually leaving your stuff_3
array empty.If you don't want these variations, you can still use pop
like so:
stuff_3.times do |i|
#pops the last item off of stuff_3
next_one = stuff_3.pop
# puts adding (next_one)
puts "Adding #{next_one}"
# adds (next_one) to array of stuff
stuff.push(next_one)
# ends for loop
end
This code uses stuff_3.times
to execute a loop as many times as there are items in stuff_3. So if stuff_3 has 3 items in it, the loop executes 3 times. The variable i
is a counter that starts at 0 and counts up each time the loop is executed. In this case, times
works much more like the traditional for-loop and the above code will execute pretty much exactly like you expected yours to execute.
Upvotes: 0
Reputation: 28305
You are shadowing the outer variable:
stuff_3.each do |stuff_3|
stuff_3.pop
Within this block of code, the "inner" stuff_3
variable takes precedence over the "outer" stuff_3
variable.
That's why you are seeing an error message:
undefined method `pop' for "Corn":String
Shadowing outer variables like this is generally considered bad practice, as it leads to confusing code and "unexpected" behaviour (like what you found here!). A simple fix is to just use a different variable name:
stuff_3.each do |stuff_3_item|
stuff_3.pop
...Although I think what you're actually trying to do here should be written a little differently - e.g.
stuff_3.each do |stuff_3_item|
puts "Adding #{stuff_3_item}"
stuff.push(stuff_3_item)
end
Upvotes: 2