interpolate a string with an incrementing number - ruby

Im trying to interpolate a string with a number and increment that number as I iterate a list of attendees...
String "Hello #{name}. You are guest number #{num}." I know how to iterate each guest name, but how do I get a number to increment within the string from 1..7? I know this is simple. I am very beginner.
Tried everything.
def assign_rooms(attendees)
room_assignments = []
rooms.to_s = [1, 2, 3, 4, 5, 6, 7]
attendees.each_with_index {|x| room_assignments<< "Hello #{x}! You'll be assigned room #{rooms}!"}
room_assignments
end

Enumerable#each_with_index gives two arguments to the block: the item itself (the attendee), and the item's index which increments as desired. You can also combine with Array#map to avoid having to explicitly create and append the output to a second array. Enumerator#with_index allows specifying a start offset for the index (so that the room numbers are not zero-based).
def assign_rooms(attendees)
attendees.map.with_index(1) {|attendee, room|
"Hello #{attendee}! You'll be assigned room #{room}!"
}
end
puts assign_rooms(['Alice', 'Bob', 'Jack'])
Outputs:
Hello Alice! You'll be assigned room 1!
Hello Bob! You'll be assigned room 2!
Hello Jack! You'll be assigned room 3!

You could also achieve your goal with an enumerator. Suppose the first room number is 2.
attendees = %w| Flora Billy-Bob Hank Trixie Herb |
#=> ["Flora", "Billy-Bob", "Hank", "Trixie", "Herb"]
nbr = 2.step
#=> (2.step)
nbr.is_a?(Enumerator)
#=> true
attendees.each { |name|
puts "Hello, #{name}, you've been assigned to our best room, number #{nbr.next}!" }
Hello, Flora, you've been assigned to our best room, number 2!
Hello, Billy-Bob, you've been assigned to our best room, number 3!
Hello, Hank, you've been assigned to our best room, number 4!
Hello, Trixie, you've been assigned to our best room, number 5!
Hello, Herb, you've been assigned to our best room, number 6!
Note the following.
nbr.next #=> 7
nbr.next #=> 8
nbr.rewind
nbr.next #=> 2
See Numeric#step, Enumerator#next and (only if you are curious) Enumerator#rewind.

For fun, I'd like to show a more structured way, using a Hash as a model for storing information.
Let's use these input data:
rooms = [110, 111, 112, 210, 211, 212].map { |n| [n, {free: true, host: nil}] }.to_h
reception_queue = %w|Kirk Picard Spock McCoy Riker LaForge|
Let's say one room is already booked:
rooms[210] = {free: false, host: 'Uhura'}
rooms #=> {110=>{:free=>true, :host=>nil}, 111=>{:free=>true, :host=>nil}, 112=>{:free=>true, :host=>nil}, 210=>{:free=>false, :host=>"Uhura"}, 211=>{:free=>true, :host=>nil}, 212=>{:free=>true, :host=>nil}}
Now we want to assign the free rooms to each attendee. It is required to find the first room available matching some condition (free, but in can be one bed, two beds, suite, etc, depending on the complexity of the model).
This can be achieved by Enumerable#find:
rooms.find { |_, v| v[:free] }
#=> [110, {:free=>true, :host=>nil}]
Once we find a room, we want to assign the host there, removing her from the queue (Array#shift). Breaking the loop if there is no match.
For example:
reception_queue.size.times do |person|
nr = rooms.find { |_,v| v[:free] }
break unless nr
rooms[nr[0]][:host] = reception_queue.shift
rooms[nr[0]][:free] = false
end
So you can end up with:
rooms #=> {110=>{:free=>false, :host=>"Kirk"}, 111=>{:free=>false, :host=>"Picard"}, 112=>{:free=>false, :host=>"Spock"}, 210=>{:free=>false, :host=>"Uhura"}, 211=>{:free=>false, :host=>"McCoy"}, 212=>{:free=>false, :host=>"Riker"}}
reception_queue #=> ["LaForge"]

Related

Optimize print output where i use check on zero. Ruby

Currently, I'm having print like this
print ((stamp_amount[0], 'first mark') unless stamp_amount[0].zero?), (', ' if !stamp_amount[0].zero? && !stamp_amount[1].zero?),
((stamp_amount[1], 'second mark') unless stamp_amount[1].zero?)
stamp_amount is an array with 2 integer values
Let's say in the current situation stamp_amount[0] = 10 and stamp_amount[1] = 3
Output preview:
10 first mark, 3 second mark
So if stamp_amount[0] = 0 the 10 first mark, part won't be show. Same if stamp_amount[1] = 0 the , 3 second mark part won't be shown
For me, it seems a little bit incorrect in terms of theory. Could you please suggest me the more correct or less painful print of this? :)
Cheers!
Your code is trying to join a sequence of up to two elements with a separator. The joining is a solved problem, see Array#join.
The problem can be then reduced to "how can I produce the correct sequence, given my stamp_amount input". Now this can be done in a thousand ways. Here's one:
def my_print(stamp_amount)
ary = [
!stamp_amount[0].zero? && stamp_amount[0],
!stamp_amount[1].zero? && stamp_amount[1],
].select{|elem| elem }
ary.join(', ')
end
my_print([10, 3]) # => "10, 3"
my_print([0, 3]) # => "3"
my_print([10, 0]) # => "10"
my_print([0, 0]) # => ""
Here's another
ary = []
ary << stamp_amount[0] unless stamp_amount[0].zero?
ary << stamp_amount[1] unless stamp_amount[1].zero?
ary.join(', ')
Here's yet another. This version can handle stamp_amount of any length.
ary = stamp_amount.reject(&:zero?)
ary.join(', ')
I'd go with the third, but the second one may be the easiest to understand for a beginner.
Use the select, as an alternative to reject (shown in part 3 of the answer by Sergio Tulentsev). It is just asa readable, and depending on the context and on the future changes to the code, you may prefer one versus the other.
puts stamp_amount.select{ |a| !a.zero? }.join(", ")
A few examples of inputs and outputs are:
stamp_amount output
--------------------------------------------------------------------------
10, 3 10, 3
10, 0 10
0, 3 3
0, 0 (prints an empty line, because the selected array is empty)
You're calculating zero? on index points more often than is needed, but the first thing I would look at refactoring here is the readability of the code. It might be nicer to calculate the message to print outside of the print method and explain what is happening with variable names.
# rubocop is going to complain about variable assignment like this
first_amount, second_amount = *stamp_amount
We can actually use the reason rubocop prefers the .zero? over == 0 or .empty? method to guide our development. zero? is in essence just empty? but it communicates the meaning of what you are attempting to do in a better manner. I would use this reasoning when assigning strings to variables that explain what they are doing.
some_name_that_explains_what_this_is_0 = "#{first_amount} piecu centu marka"
some_name_that_explains_what_this_is_1 = "#{second_amount} tris centu marka"
Your current code is confusing as you have the possibility of printing a string like "10 tris centu marka" which does not make lexical sense and probably not what you are after considering tis evaluates to 'second mark', which would pose an issue if the first value is zero. We also could reject zero integers before we start converting them to strings.
array = [1, 0].reject(&:zero?)
Now we can take the array and do something like:
string = []
array.each_with_index { |e, i| string << "#{e} #{Ordinalize.new(i).ordinalize} mark" }
message = string.join(', ')
print(message)
# ord class
class Ordinalize
def initialize(value)
#value = value
end
def ordinalize
mapping[#value]
end
def mapping
# acounting for zero index
['first', 'second']
end
end
where we are calculating the ordinalization and letting our new class handle the sentence structure for us.
Outputs:
[1, 0] => "1 first mark"
[0, 1] => "1 first mark"
[1, 2] => "1 first mark, 2 second mark"

Creating and adding to arrays within a program -- Ruby

I'm a fairly new Ruby user and I was wondering how you create and edit arrays within a program. I'm making a sentence-generator-type program where you can add to arrays of nouns, verbs, and other sentence parts, but I'm currently not sure how to make the arrays in the first place.
Here's the code:
#!/usr/bin/ruby Make_a_Sentence!
#This will eventually create the sentence
def makeSent (*arg)
for i in 0...arg.length
print arg[i].sample if arg[i].kind_of?(Array)
print arg[i] if arg[i].kind_of?(String)
end
end
#this is supposed to add to the array (it's not working)
def addWord (array, word)
if array.kind_of?(Array)
array.push(word)
puts "#{ word } added to #{ array }"
else
puts "#{ array } does not exist"
end
end
#This is supposed to create the arrays
def addType (name)
#name = Array.new
puts "#{ name } created"
end
while 1 > 0
input = gets
$words = input.split
if $words[0] == "addWord" && $words.length == 3
addWord($words[1], $words[2])
end
if $words[0] == "addType" && $words.length == 2
addType($words[1])
end
end
**Sorry! I guess I didn't phrase the question well enough! I was mainly wondering how to create new arrays while the program is running, but the arrays have specific names that are given. I actually ended up just using hashes for this, but thanks for the responses nonetheless!
Making an Array is done like so:
array = ["val1","val2","val3"]
array = %w{value value value}
# The second example is a shorthand syntax (splits values with just a space)
Familiarize yourself with the documentation: http://www.ruby-doc.org/core-2.1.0/Array.html
Also when you're using methods like: def makeSent (*arg) just be aware that *args with the * in front of it - is what's called a splat. Which means this method takes many arguments without the [] syntax and automatically converts all of the arguments into an array for you.
So you would call this method like: makeSent (first, second, third, etc)
Creating arrays is easy: simply enclose the objects in square brackets, like so:
my_array = [12, 29, 36, 42]
another_array = ['something', 64, 'another', 1921]
Notice you can mix and match types within the array. If you want to add items to the end of the array, you can use << to do it:
my_array = [1, 3, 5, 7]
my_array << 9
# my_array is now: [1, 3, 5, 7, 9]
You can access and modify specific items within the array by indexing it within square brackets:
my_array[2] # <-- this will be 5, as arrays are 0-indexed
my_array[2] = 987
# my_array is now [1, 3, 987, 7, 9]
There are a lot of great methods for accessing, modifying, and comparing arrays that you can find in the documentation.

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

Ruby programming assignment

So i need some support with my Ruby assignment, I'm not from US so you have to excuse my English.
We are building a hotel and this is the second assignment. It's a console based application and I have a class called main.rb that handles the runtime and a guest-class.
In this second assignment we are to preload the app with five guest-objects, I guess I have to use an array but don't really know how. Below are my guest class and my main class is simply a while-loop with a case statement.
I need help with:
adding 5 guests (not to a db or textfile only to a array or so) when the program starts
the hotel has 20 rooms and i need to randomize the room number and exclude already rented rooms
Hope you can help! Thanks!
class Guest
#Instance variables.
attr_accessor :firstName,
:lastName,
:address,
:phone,
:arrival,
:plot,
:gauge
#Constructor sets the guest details.
def initialize(first, last, adress, phone, arrival)
#firstName = first
#lastName = last
#address = address
#phone = phone
#arrival = arrival
#plot = range_rand(1,32)
#gauge = range_rand(2000,4000)
end
#Using rand()-method to randomize a value between min and max parameters.
def range_rand(min,max)
min + rand(max-min)
end
def to_string
"Name = #{#firstName} , Plot = #{#plot}"
end
end
Creating an array:
number_array = [1, 2, 3, 4, 5]
Accessing the elements of an array:
number_array[2]
# this would return the integer 3
Adding a new element to an array:
number_array << 6
# this would return [1, 2, 3, 4, 5, 6]
You can create a new guest by doing something like this:
Guest.new("John", "Doe", "1500 main street", "123-456-7890", "1/1/2010")
Since this is a homework assignment, I'll leave it to you to combine everything into a working solution ;)
Other people have already answered the first part of your question, so I'll help you with the second one (I'll provide the minimum, so that you still have some work to do :) )
You could create an array containing the 20 room numbers :
empty_rooms = (1..20).to_array
Then for each guest :
1) Take a random number in this array ( hint : randomize the index )
2) Remove this number from the array
3) And assign the room number to a Guest
4) Add the guest to the array of guests
I think what you mean is that you want 5 guest objects. You could put them in an array by creating an array literal and then adding guests to it
#guests = []
#guests << Guest.new()
#guests << Guest.new()
now your #guests array has two guests, etc.

ruby loop next case example

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)

Resources