ruby loop next case example - ruby

I am trying to loop through multiple statements, but want to go through each one once, example:
while count < 5 do
count+= (not sure if this how ruby increments counts)
puts "In condition one"
next if count > 1
puts "In condition two"
next if count > 1
#..
end
Update 1:
Thanks for the reply, what I'm trying to do is loop through an array and have each element of the array be applied to 10 different conditions. For example: array[has 100 elements] element 1 gets condition 1, element 2 goes on to condition 2, and so on. Since there are 10 conditions, the 11th element in the array would get condition 1 again, and so on (condition 1 condition 2 condition 3 ...)
Update 2:
Thanks again for taking the time to reply. I apologize that I'm not being very clear. The array contains emails. I have 10 email servers and want to send the 200 emails I have in my array through each server (only 1 email per server). I hope that makes sense

If I'm reading you correctly, you want to send a large number of emails through a small number of servers while balancing the load. Try creating a class to manage the servers (here's the basic idea)
class ServerFarm
def initialize
#servers = []
end
attr_accessor :servers
def add_server(server)
#servers << server
end
def remove_server(x)
if x.is_a?(Numeric) then
#servers.delete_at(x)
elsif x.is_a?(Server)
#servers.delete(x)
end
end
def server_available?
#servers.each {|s| return true if s.available? }
false
end
def dispatch_message(email)
#servers.each_with_index {|s, i|
next unless s.available?
s.dispatch(email)
return i
}
nil
end
end
Now, all you will have to do is call ServerFarm.dispatch_message for an email and it will be sent using one of the available servers. This class assumes that you have a class named Server that holds the info for your individual servers, etc etc.

array = (1..100).to_a
conditions = (1..10).to_a
array.each_with_index do |elem, i|
puts "element %d, using condition %d" % [elem, conditions[i % conditions.length]]
end
produces
element 1, using condition 1
element 2, using condition 2
element 3, using condition 3
element 4, using condition 4
element 5, using condition 5
element 6, using condition 6
element 7, using condition 7
element 8, using condition 8
element 9, using condition 9
element 10, using condition 10
element 11, using condition 1
element 12, using condition 2
etc.

Does this help? I can't tell what you are trying to do.
5.times do |count|
puts 'In condition ' + %w(one two three four five)[count]
end
The 5.times do |count| will excecute the block five times with count starting at zero and incrementing each time. %w(one two three four five) is the same as ["one", "two", "three", "four", "five"].
If you want to do five different things consecutively, you do not need a loop. Just put the statements in a row:
# do thing 1
# do thing 2
# do thing 3
# ...
Edit:
"I have an array that I want to loop through, but each element in the array needs to go through a different condition each time and then restart at the first condition."
To loop through an array endlessly, testing each element against conditions:
arr = ['sdfhaq', 'aieei', 'xzhzdwz']
loop do
arr.each do |x|
case x
when /..h/
puts 'There was a \'h\' at the third character.'
when /.{6}/
puts 'There were at least six characters.'
else
puts 'None of the above.'
end
end
end
Edit 2:
"Thanks for the reply, what I'm trying to do is loop through an array and have each element of the array be applied to 10 different conditions, example: array[has 100 elements] element 1 gets condition 1 element 2 goes on to condition 2 and so on, since there are 10 conditions the 11th element in the array would get condition 1 again and so on. condition 1 condition 2 condition"
You will need to use the % method on numbers.
arr = Array.new(130) # an array of 130 nil elements.
num_conditions = 10
arr.each_with_index do |x, i|
condition = (i + 1) % num_conditions
puts "Condition number = #{condition}"
end
More information: http://ruby-doc.org/core/classes/Fixnum.html#M001059
Edit three:
def send_an_email(email, server)
puts "Sending an email with the text #{email.inspect} to #{server}."
end
email_servers = ['1.1.1.1', '2.2.2.2']
emails = ['How are you doing?', 'When are you coming over?', 'Check out this link!']
emails.each_with_index do |email, i|
send_an_email email, email_servers[i % email_servers.length]
end
You can modify email_servers and emails and have it still work, even if the lengths are changed.

array.each_slice(10) do |emails|
servers.zip(emails) { |server,email| server<<email }
end
(Ruby 1.9.2)

Related

cant understand the triagle number enumerator yielder yield

unable to understand this code how it works.
triangular_numbers = Enumerator.new do |yielder|
number = 0
count = 1
loop do
number += count
count += 1
yielder.yield number
end
end
5.times { print triangular_numbers.next, " " }
cant understand how yielder works for this block. how its yield work for number variable. how do loop runs 5 times. and how triangular_number.next works for the first time.
An enumerator is basically something that you can call next on and get something back. The yielder is the mechanism where it gives something back when next is called. Execution stops at the yield until the next call to next.
Farfetched analogy
You can think of an enumerator as a ticket machine like when you're waiting in line at a government office. When you press a button (next) it gives you a ticket. Inside the machine there's a chute where the ticket comes out. But the ticket machine is not constantly printing tickets. It waits for the button to be pressed before it prints the next ticket and puts it through the chute.
In this case the analogous code would be:
ticket_machine = Enumerator.new do |chute|
ticket = 0
loop do
#print_ticket
chute.yield ticket #waits here until you hit the button
ticket += 1
end
end
5.times { print ticket_machine.next, " " } # gets 5 tickets
Your code sample is basically the same thing, but instead of issuing tickets, it's issuing triangular numbers. The chute is the yielder where the numbers get passed through.
This is not the only way to use an enumerator, check the docs for more.
Enumerator::new accepts a block. This block, when run, receives an Enumerator::Yielder, which has a method #yield.
When the Enumerator#next is called, the block is executed, up to the first Enumerator::Yielder#yield. The execution is paused there; the value given to yield is the value that next returns. When you call next again on the same Enumerator, the execution is resumed, and proceeds until it encounters yield again.
So in your case, 5.times executes its block, intending to repeat it five times. triangular_numbers.next is called; this starts the execution of the block above. number and count are set to their values, and an infinite loop is started. number is set to 1, count is set to 2, and then we find yielder.yield. This pauses the execution of the block, and returns the control back to where next was called inside 5.times loop. next returns 1, because yielder.yield received number (1).
Second time through the 5.times loop, we want to print the next number. This stops the main execution line, and resumes the enumerator block from just after yielder.yield. The infinite loop continues; number is 3, count is 3, yielder.yield pauses the enumerator and resumes the main code. next gets 3, which gets printed.
Third, fourth and fifth time through the 5.times loop are exactly the same.
After five iterations, 5.times loop ends, and the execution proceeds past it. The enumerator is paused, ready to give the next number in sequence, if you ever call next on it again (since it has an infinite loop), but you never do, and the program exits.
I'll try to explain what it does bit by bit, so you can try to wrap your head around it.
Enumerator.new do |yielder|
end
So you instantiate an enumerator that will work over a variable called yielder.
Inside its scope you set some local vars (that will be kept as the object is reused):
number = 0
count = 1
And then you set a loop that increments number by count and count by 1 and then call yield over your argument passing number to it as an argument.
loop do
number += count
count += 1
yielder.yield number
end
5.times repeats the block passed to it 5 times. The block
-> { print triangular_numbers.next, " " }
calls print that takes n args and concatenates the parts to form a string, but does not append a newline.
The first argument is our enumerator next interaction (triangular_numbers.next), which will compute the current number and call yield on the Enumerator::Yielder that's implicitly created handling the control back to the calling Fiber along with any args that got passed to it.
(All Enumerators are implemented as "Fibers" on MRI)
So that yielder.yield call is similar to a Fiber.yield call and will allow the 5.times loop to run and return the number 1.
I'm adding a piece of code to the already clear explanation provided:
my_enum = Enumerator.new do |whatever_name_for_the_yielder|
n = 0
loop do
whatever_name_for_the_yielder.yield "Return this: #{n}"
n += 1
end
end
puts my_enum.next #=> Return this: 0
puts my_enum.next #=> Return this: 1
puts my_enum.next #=> Return this: 2
When you provide an end to the iteration, it stops with an error:
my_enum2 = Enumerator.new do |whatever_name_for_the_yielder|
2.times do |n|
whatever_name_for_the_yielder.yield "Return this: #{n}"
end
puts "Outside the loop"
end
puts my_enum2.next #=> Return this: 0
puts my_enum2.next #=> Return this: 1
puts my_enum2.next #=> Outside the loop
#=> ERROR: .....in `next': iteration reached an end (StopIteration)

Count how many times a method can be called until it returns nil

Background: I need to determine the number of records in a remote location. The records are sequentially numbered (starting at 0, no gaps) and can only be fetched one by one based on their number.
The method to fetch the records over the network returns a truthy value upon success and a falsey value upon failure. Here'a an example:
fetch_record(0) #=> true (record #0 exists)
fetch_record(1) #=> true (record #1 exists)
fetch_record(2) #=> true (record #2 exists)
fetch_record(3) #=> nil (no record #3)
I'm trying to find an elegant way to count how many times I can call fetch_record with increasing argument until it returns nil (3 times in the above example).
What I've tried so far
Given this simplified implementation for testing purposes:
def fetch_record(index)
raise if index > 3 # just to prevent endless loops
true if index < 3
end
Here are my attempts:
A while loop with an explicit counter variable would obviously work, but I doesn't look very idiomatic:
i = 0
i += 1 while fetch_record(i)
i
#=> 3
I could use step and break with an explicit result value but that looks cumbersome:
0.step(by: 1).each { |i| break i unless fetch_record(i) }
#=> 3
A lazy enumerator is even worse:
0.step(by: 1).lazy.map { |i| fetch_record(i) }.take_while(&:itself).count
#=> 3
Is there an easier way to accomplish the above?
Assuming you are free to implement fetch_record the other way round, to return true for inexisting record:
def fetch_record index
index >= 3
end
(0..Float::INFINITY).detect &method(:fetch_record)
#⇒ 3
take_while anyone?
records = 0.step.take_while{|i| fetch_record(i)}

How to modify an iterator while iterating over an array

I want to skip a loop x times according to a condition that is determined at runtime. How can I do this?
for i in (0..5)
if i==0
3.times {next} # i=i+3 also doesnt work
end
puts i
end
Expect to output
3
4
5
EDIT:
To clarify, the question is both the condition (ie i==0) and skipping x times iteration are determined dynamically at runtime, more convoluted example:
condition = Array.new(rand(1..100)).map{|el| rand(1..10000)} #edge cases will bug out
condition.uniq!
for i in (0..10000)
if condition.include? i
rand(1..10).times {next} # will not work
end
puts i
end
simple method to skip by a defined multiple.
array_list = (0..5).to_a
# Use a separate enum object to hold index position
enum = array_list.each
multiple = 3
array_list.each do |value|
if value.zero?
multiple.times { enum.next }
end
begin puts enum.next rescue StopIteration end
end

Skipping iteration from Proc

I'm wondering if it's possible to use a Proc for skipping iteration in Ruby?
I wrote some piece of code
def validation i
pr = Proc.new do |i|
if i < 3
next
end
end
pr.call(i)
end
(1..5).each do |i|
validation i
puts "#{i} is bigger than 3"
end
and I expected something like this as result:
3 is bigger than 3
4 is bigger than 3
5 is bigger than 3
but instead I got:
1 is bigger than 3
2 is bigger than 3
3 is bigger than 3
4 is bigger than 3
5 is bigger than 3
So is it possible to use somehow next in Proc for skipping from outer iteration or there is some other way?
You can't call next in your validation method because the loop is external. What you can do is use next within your (1..5).each loop that's dependent on a call to validation. The following code produces your desired result.
Edit - The code has been refactored to make appropriate use of Proc.
pr = Proc.new {|i| i < 3}
(1..5).each do |i|
next if pr.call(i)
puts "#{i} is bigger than 3"
end

Usage of Pipes in Ruby Loops

So, maybe I'm over-complicating something that isn't that hard, but here goes.
In Ruby, there's a method of looping called .each. I think that this is very cool--but what I'm finding less cool is the amount of stuff written about the pipe that comes after it (or any other do-type loop in Ruby, it would seem). Sometimes there is a single thing in the pipe:
basket.each do |fruit|
puts "This is a #{fruit}.\n"
end
But sometimes, there are two things in this pipe, like so:
contacts.each do |name, profession|
puts "#{name} is a #{profession}.\n"
end
So what I'm wondering now, is it possible to have more than two items in that pipe? Like if I have a huge, big, and ugly multi-dim array?
What if I add things to my pipe and they're not there? Will it give the value in the pipe nil? Or will it throw an error?
Again, sorry if this is obvious to long-time Rubyists, but I came from the land of strictly typed variables, and I'm now leaving PHP land, too. :)
EDIT
So what if I have something like this:
categories = [["Bathroom", "Bathroom Fixtures", "Plumbing"],
["Ceiling Fixtures", "Chandeliers", "Flush Mounts", "Mini Chandeliers", "Semi-Flush Mounts", "Pendants", "Track Lighting", "Mini Pendants", "Directional", "Island/Pool Table", "Recessed Lighting"],
["Outdoor", "Exterior", "Landscape Lighting"],
["Fans", "Fans", "Fan Accessories"],
["Lamps", "Lamps", "Shades"],
["Wall Fixtures", "Sconces", "Foyer/Hall Lanterns"],
["Specialty", "Undercabinet", "Light Bulbs", "Lighting Controls", "Glass", "Specialty Items", "Utility"],
["Home Decor", "Decor/Home Accents", "Furniture"]]
Can I loop through it like this?
categories.each do |category, subcats|
puts "The main category is #{category} and the sub categories are: "
subcats.each do |subcat|
puts "#{subcat}, "
end
end
Lets start with a break down of the each method.
a = [1,2,3,4,5]
a.each do |num|
puts num
end
# 1
# 2
# 3
# 4
# 5
The do ... end portion is called a block
This block accepts one parameter (an element in the array)
The way you pass parameters to a block is with |'s
If you supply more than one argument to the block:
a.each do |num, x|
puts num
puts x
end
# 1
#
# 2
#
# 3
#
# 4
#
# 5
#
x is nil for each iteration.
Lets write a method of our own that uses blocks so you can see how they work.
def my_each(a=[])
a.each do |x|
yield x if block_given?
end
end
my_each(a) do |num|
puts num
end
Here yield x is saying, execute the supplied block and pass x to it.
If you pass another parameter to your block, it is nil. Why?
Our implementation of my_each doesn't know anything about a second parameter so it does not yield anything so it remains nil.
When you have a simple array, the following things happen:
arr = [1,2,3,4]
arr.each do |x|
p x
end
1
2
3
4
=> [1,2,3,4]
arr.each do |x,y|
p x
p y
end
1
nil
2
nil
3
nil
4
nil
=> [1,2,3,4]
so if ruby doesn't know what to put into the block argument, it simply sets it to nil. Now consider a nested array:
arr = [[1,2],[3,4],[5,6]]
arr.each do |x|
p x
end
[1, 2]
[3, 4]
[5, 6]
=> [[1,2],[3,4],[5,6]]
arr.each do |x,y|
p x
p y
end
1
2
3
4
5
6
=> [[1,2],[3,4],[5,6]]
In this case, ruby assumes that you want to assign the two elements of the inner arrays to the block variables x and y. The same thing applies to hashes, where Ruby assigns the key and value to x and y:
hash = {1 => 2, 3 => 4, 5 => 6}
hash.each do |x,y|
p x
p y
end
1
2
3
4
5
6
=> {1=>2,3=>4,5=>6}
When you don't have enough elements in the nested arrays, the block variables are assigned nil, indeed. When there are too many of them, they are simply discarded:
arr = [[1,2,3],[4,5],[6]]
arr.each do |x,y|
p x
p y
end
1
2
4
5
6
nil
=> [[1,2,3],[4,5],[6]]
pretty straightforward!
EDIT:
As for your edited question: no, you cannot apply this 1:1 to Ruby code, you would have to manually apply the splat operator (*) to subcats. This way, ruby assigns all remaining elements to the 'splatted' block variable:
categories.each do |category,*subcats|
puts "The main category is #{category} and the sub categories are: "
subcats.each do |subcat|
puts "#{subcat}, "
end
end
although i would generate a comma-separated list of subcategories like this:
categories.each do |category,*subcats|
puts "The main category is #{category} and the sub categories are: "
puts subcats.join(', ')
end
EDIT 2:
Oh, and you would not handle a huge ugly evil multidimensional array by defining a lot of block parameters for its elements. You probably would iterate through it using nested loops as in almost every other language, if only because you never know how many elements it contains.
The pipes you are talking about is a parameter list of a block "variable". Actually that is some kind of a function pointer, and the pipes marks the parameter list.
Check the description of array.each.
This is not magic, the number of parameters is defined in the block, you can't add more than that, if you do, they won't get a value. The reason is for "sometime" there can be more than one, is that it's probably a hash.each, which has two parameters, a key and a value.
You can create your own functions with block parameters, read this.
For your iteration problem, you can use a hash, or you can write your own iterator.
Multiple Arguments to a Block
Array#each iterates over an array object, and passes either a single object into the block or returns an enumerator. You can redefine this behavior, but #each is the wrong method if you want multiple values at a time; see Enumerator#each_slice for an alternative.
Data Structures
Your problem would be easier to solve with the right data structure. Instead of an array, you should consider using a hash. For example:
categories =
{"Bathroom"=>["Bathroom Fixtures", "Plumbing"],
"Ceiling Fixtures"=>["Chandeliers", "Flush Mounts", "Mini Chandeliers"]}
categories.each do |key, value|
puts "#{key}:"
value.each { |v| puts "\t%s" % v }
end
This returns:
Bathroom:
Bathroom Fixtures
Plumbing
Ceiling Fixtures:
Chandeliers
Flush Mounts
Mini Chandeliers

Resources