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.
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"]
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 - eg
stuff_3.each do |stuff_3_item|
puts "Adding #{stuff_3_item}"
stuff.push(stuff_3_item)
end
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.