简体   繁体   中英

How to use a for loop on an array in Ruby

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM