Find specific word in array in order - ruby

I have:
ary = [
'ahorseride', 'amparkeetjump', 'acatlive', 'adogwish', 'bmparkeetjump',
'bcatlive', 'bdogwish', 'bhorseride', 'brabuffallo', 'chorseride7679',
'ceelionking5454', 'crabuffallokjkj4', 'dgiraffeoiu9-0', 'chorseride767',
'ccatlive', 'dcatlive', 'ddogwish', 'emparkeetjump', 'emouse', 'eeelionking',
'erabuffallo', 'ffhorseride7679', 'fgeelionking5454', 'fhcrabuffallokjkj4a',
'fkcgiraffeoiu9087*s',
]
big_animal = ['horse', 'lion', 'buffallo', 'giraffe']
For each element in big_animal, I would like to find which elements of ary include that as a substring, and present them in a specific order. I would like to achieve this result:
horse in the chorseride7679
lion in the ceelionking5454
buffallo in the crabuffallokjkj4
giraffe in the dgiraffeoiu9-0
and/or
horse in the ffhorseride7679
lion in the fgeelionking5454
buffallo in the fhcrabuffallokjkj4a
giraffe in the fkcgiraffeoiu9087*s
How do I do this? My attempt is:
horse = big_animal[0]
ary.each do |e|
puts "#{horse} in the house of #{e}" if e.include?(horse)
end
whose result is:
horse in the house of ahorseride
horse in the house of bhorseride
horse in the house of chorseride7679
horse in the house of chorseride767
horse in the house of ffhorseride7679

It appears that you wish to find four consecutive elements of ary that respectively include the strings given as elements of big_animal. If that is true all such consecutive elements from ary could be obtained as follows.
ary.each_cons(big_animal.size).select do |words|
big_animal.each_index.all? { |i| words[i].include?(big_animal[i]) }
end
#=> [["chorseride7679", "ceelionking5454", "crabuffallokjkj4", "dgiraffeoiu9-0"],
# ["ffhorseride7679", "fgeelionking5454", "fhcrabuffallokjkj4a",
# "fkcgiraffeoiu9087*s"]]
See Enumerable#each_cons.

First of all, I have to admit that this is the very first time I'm using Ruby which is why my code might be a bit messy.
On a second note, I've seen the answer from nPn getting the output you want to have with a regex. However, this is not completely solving your problem because his solution will only retrieve values containing the big_animal and ending with a digit. This solution does not care about the right order of the houses.
As far as I've understood your problem, you want all the houses that contain the big_animal but only if they are in the same order. This is why I've come up with this piece of code:
ary = ['ahorseride', 'amparkeetjump', 'acatlive', 'adogwish', 'bmparkeetjump', 'bcatlive', 'bdogwish', 'bhorseride', 'brabuffallo', 'chorseride7679', 'ceelionking5454', 'crabuffallokjkj4', 'dgiraffeoiu9-0', 'chorseride767', 'ccatlive', 'dcatlive', 'ddogwish', 'emparkeetjump', 'emouse', 'eeelionking', 'erabuffallo', 'ffhorseride7679', 'fgeelionking5454', 'fhcrabuffallokjkj4', 'fkcgiraffeoiu9087', ]
big_animal = ['horse', 'lion', 'buffallo', 'giraffe']
count = 0
houses = Array.new(big_animal.length)
while count < ary.length do
animals = 0
if ary[count].include?(big_animal[animals])
while animals < big_animal.length do
if ary[count+animals].include?(big_animal[animals])
houses[animals] = ary[count+animals]
if animals == big_animal.length-1
puts houses
end
else
houses = Array.new(big_animal.length)
end
animals = animals + 1
end
end
count = count + 1
end
The above code gives me the following output:
chorseride7679
ceelionking5454
crabuffallokjkj4
dgiraffeoiu9-0
ffhorseride7679
fgeelionking5454
fhcrabuffallokjkj4
fkcgiraffeoiu9087
You can also try it online here and change the input array on that website to test different scenarios. I'd be happy to know if this is what you were searching for.

Just guessing that you are looking for the first animal with the matching name , ending in a digit.
ary = ['ahorseride', 'amparkeetjump', 'acatlive', 'adogwish', 'bmparkeetjump',
'bcatlive', 'bdogwish', 'bhorseride', 'brabuffallo', 'chorseride7679',
'ceelionking5454', 'crabuffallokjkj4', 'dgiraffeoiu9-0', 'chorseride767',
'ccatlive', 'dcatlive', 'ddogwish', 'emparkeetjump', 'emouse', 'eeelionking',
'erabuffallo', 'ffhorseride7679', 'fgeelionking5454', 'fhcrabuffallokjkj4', 'fkcgiraffeoiu9087' ]
big_animal = ['horse', 'lion', 'buffallo', 'giraffe']
big_animal.each do |ba|
rs = "#{ba}.*\\d$"
rexp = Regexp.new rs
entry = ary.find { |i| i.match(rs) }
puts "#{ba} in the house of #{entry}"
end
#horse in the house of chorseride7679
#lion in the house of ceelionking5454
#buffallo in the house of crabuffallokjkj4
#giraffe in the house of dgiraffeoiu9-0

https://repl.it/NxGr
By brazenly overriding String method. Better do not use this approach -))
class String
def in_house? other
(other.is_a? String) && (other.include? self)
end
def in_house_message_for other
puts "#{self} in house of #{other}" if self.in_house? other
return
end
end
then
ary.each do |house|
big_animal.each do |animal|
animal.in_house_message_for(house)
end
end
or that way to print in same order with big_animal array
big_animal.each do |animal|
ary.each do |house|
animal.in_house_message_for(house)
end
end
the second one will print
horse in house of ahorseride
horse in house of bhorseride
horse in house of chorseride7679
horse in house of chorseride767
horse in house of ffhorseride7679
lion in house of ceelionking5454
lion in house of eeelionking
lion in house of fgeelionking5454
buffallo in house of brabuffallo
buffallo in house of crabuffallokjkj4
buffallo in house of erabuffallo
buffallo in house of fhcrabuffallokjkj4
giraffe in house of dgiraffeoiu9-0
giraffe in house of fkcgiraffeoiu9087

Related

formatting an entire line in ruby given multiple arguments

For my program, I have a list of things that's sort of like a spreadsheet that I'm trying to print to the console. I want it to look something like
garage 1
car: jaguar
price: $134,000
car: mercedes
price: $234,000
garage 2
car: jaguar
price: $134,000
garage is a hash with each key being garage number and cars in a specific garage being pushed to each key, eg garage[0] = [car1, car2]
Class car holds the information printed, and has these definitions
class Car
attr_accessor :car_name, :car_price
def initialization(name, price)
#car_name = name
#car_price = price
end
def name
#car_name
end
def price
#car_price
end
end
I've tried to implement such
garage.each do |x|
print "garage "
print garage.index(x) + 1
puts " "
x.each do |y|
printf("%10s", "Car: ")
puts y.name.rjust(16)
printf("%10s", "Price: $")
puts y.price.to_s.rjust(16)
puts " "
end
end
But I'm not getting the desired output. Depending on the car name, the spaces in between will be too far and the output won't be aligned.
I'm wondering if there's a method where I can format an entire line output? Basically like chaining together strings and other variables such as integers and floats and setting their distances correctly so that I can customize each line that's output to console.
First create the Car class:
class Car
attr_accessor :car_name, :car_price
def initialize(name, price)
#car_name = name
#car_price = price
end
end
Note that the method is initialize, not initialization, and that the class method attr_accessor creates both getter and setter methods for each of the two instance variables, so there is no need for the explicit getter methods.
Now let's create a couple of instances of this class.
jag = Car.new('jaguar', 134000)
#=> #<Car:0x00007f839690d2d0 #car_name="jaguar", #car_price=134000>
mb = Car.new('mercedes', 234000)
#=> #<Car:0x00007f83990b1d90 #car_name="mercedes", #car_price=234000>
Suppose the hash garage is as follows.
garage = { 1=>[jag, mb], 2=>[jag] }
#=> {1=>[#<Car:0x00007f839690d2d0 #car_name="jaguar", #car_price=134000>,
#=> #<Car:0x00007f83990b1d90 #car_name="mercedes", #car_price=234000>],
# 2=>[#<Car:0x00007f839690d2d0 #car_name="jaguar", #car_price=134000>]}
Firstly, we will need a way to display car prices in the correct format, starting with a dollar sign and with thousands separated by commas. One way to do that is as follows.
def convert_price_to_str(car_price)
s = ''
loop do
car_price, three = car_price.divmod(1000)
break "$#{three}#{s}" if car_price.zero?
s = (",%03d" % three) + s
end
end
For example,
convert_price_to_str 34 #=> "$34"
convert_price_to_str 3042 #=> "$3,042"
convert_price_to_str 49621 #=> "$49,621"
convert_price_to_str 1324534 #=> "$1,324,534"
See Integer#divmod, a very handy (and underutilized) method.
To display the values in the desired format it is necessary to determine the length of the longest car name or formatted price.
longest = garage.values.flatten.map do |car|
[car.car_name.size, convert_price_to_str(car.car_price).size].max
end.max
#=> 8
Now let's write another helper method to display car names and prices.
def print_name_and_price(name, price, longest)
puts " car: #{name.rjust(longest)}"
puts " price: #{convert_price_to_str(price).rjust(longest)}"
puts
end
For example,
print_name_and_price('jaguar', 134000, longest)
displays the three lines (the last being empty):
car: jaguar
price: $134,000
See String#rjust.
We may now put all this together.
garage.each do |g,cars|
puts "garage #{g}"
cars.each do |car|
print_name_and_price(car.car_name, car.car_price, longest)
end
end
displays:
garage 1
car: jaguar
price: $134,000
car: mercedes
price: $234,000
garage 2
car: jaguar
price: $134,000

Rock paper scissor - homework

In a game of rock-paper-scissors, each player chooses to play Rock (R), Paper (P), or Scissors (S). The rules are: Rock breaks Scissors, Scissors cuts Paper, but Paper covers Rock. In a round of rock-paper-scissors, each player's name and strategy is encoded as an array of two elements. Create a RockPaperScissors class with a class method winner that takes two 2-element arrays like those above, and returns the one representing the winner:
RockPaperScissors.winner(['Armando','P'], ['Dave','S']) # => ['Dave','S']
If either player's strategy is something other than "R", "P" or "S" (case-SENSITIVE), the method should raise a 'RockPaperScissors::NoSuchStrategyError' exception and provide the message: "Strategy must be one of R,P,S"
If both players use the same strategy, the first player is the winner.
I have my code below. My code is not comparing the two strings correctly in the line
(#p1[1,1]==rules["#{p}"]?#p1:#p2).
Please help me out.
class RockPaperScissors
def winner(p1,p2)
#p1 = p1
#p2 = p2
p = (#p1[1,1]+#p2[1,1]).sort
rules = Hash.new(0)
rules = {"PR"=>"R","PS"=>"S", "RS"=>"R", "PP"=>"1","RR"=>"1","SS"=>"1"}
if rules["#{p}"].nil?
raise RockPaperScissors::NoSuchStrategyError,"Strategy must be one of R,P,S"
elseif rules["#{p}"]=="1"
return #p1
else
print #p1[1,1]
print rules["#{p}"]
#p1[1,1]==rules["#{p}"]?#p1:#p2
end
end
end
t = RockPaperScissors.new
print t.winner(['Armando','R'], ['Dave','S'])
Some general tips: You don't need [1,1], [1] or .last would be better. Also no need to initialize rules to a new hash, you can only keep the line rules = {"PR"=>"R".... puts is more commonly used than print. You're overthinking this a bit. Maybe clean up your code, try to simplify it with the tips posted above and see if this gets you unstuck.
Once you are done, have a look at what an idiomatic Ruby solution could look like, but don't submit it as your solution:
module RockPaperScissors
VALID_STRATEGIES = %i(R P S)
RULES = { R: :S, P: :R, S: :P }
def self.winner(p1, p2)
choice1, choice2 = p1.last.intern, p2.last.intern
unless [choice1, choice2].all? { |s| VALID_STRATEGIES.include? s }
raise RockPaperScissors::NoSuchStrategyError, "Strategy must be one of R,P,S"
end
return p1 if choice1 == choice2
RULES[choice1] == choice2 ? p1 : p2
end
end
When you use the [1,1] on an array, you receive an array of size 1 starting from index 1:
[1,2,3][1,1]
# => [2]
[1,2,3][1]
# => 2
Because of that when you compare it to the rules, you never get true, since no rule is an array...
["S"] == "S"
# => false
So to fix your code, instead of
p = (#p1[1,1]+#p2[1,1]).sort
# ...
(#p1[1,1]==rules["#{p}"]?#p1:#p2)
You should try:
p = (#p1[1]+#p2[1]).sort
# ...
(#p1[1]==rules[p]?#p1:#p2

Loop returns only the last item

I'm new to Ruby, so the answer is probably pretty simple. Not to me though
I am taking an array of strings (A) and matching it against another array of strings (B) to see if a given string from (A) exists as a substring within a string from B.
The compare seems to work however, I only get back a result from the last (A) string compared.
What might this be?
def checkIfAvailableOnline(film)
puts "Looking for " + film
lowerCaseFilm = film.downcase
#iterate through the linesarray scanning for the film in question
for line in #linesArray
#get the line in lowercase
lowerCaseLine = line.downcase
#look for the film name as a substring within the line
results = lowerCaseLine.scan(lowerCaseFilm)
if results.length > 0
#availableOnlineArray << results
end
end
end
#-----------------------------------------
listFilmsArray.each {|line| checkIfAvailableOnline(line)}
Given a list of film names:
FILM_NAMES = [
'Baked Blue Tomatoes',
'Fried Yellow Tomatoes',
'The thing that ate my homework',
'In a world where',
]
Then to find all film names containing a substring, ignoring case:
def find_films_available_online(partial_film_name)
FILM_NAMES.find_all do |film_name|
film_name.downcase[partial_film_name.downcase]
end
end
p find_films_available_online('tomatoes')
# => ["Baked Blue Tomatoes", "Fried Yellow Tomatoes"]
p find_films_available_online('godzooka')
# => []
To find out if a film name is available online:
def available_online?(partial_film_name)
!find_films_available_online(partial_film_name).empty?
end
p available_online?('potatoes') # => false
p available_online?('A World') # => true
To find out which of a list of partial film names are available online:
def partial_film_names_available_online(partial_film_names)
partial_film_names.find_all do |partial_film_name|
available_online?(partial_film_name)
end
end
p partial_film_names_available_online [
'tomatoes',
'potatoes',
'A World',
]
# => ["tomatoes", "A World"]
A more rubyish way to do this is:
Given an array of films we are looking for:
#films = ["how to train your dragon", "kung fu panda", "avatar"]
Given an array of lines that may contain the films we are looking for:
#lines_array = ["just in kung fu panda", "available soon how to train your dragon"]
Return the film name early if it exists in a line or false if it doesn't after searching all the lines:
def online_available(film)
#lines_array.each do |l|
l.downcase.include?(film) ? (return film) : false
end
false
end
Check for the films in the lines rejecting the ones that returned false, print them and ultimately return an array of the matches we found:
def films_available
available = #films.collect{ |x| p "Looking for: #{x}"; online_available(x) }
.reject{ |x| x == false }
available.each{|x| p "Found: #{x}"}
available
end
It is considered bad style to use camel-case in method names with Ruby but you know what they say about opinions.
.each is an internal iterator and I'm pretty sure the "for" loop will run slower than the enumerable each method that arrays inherit.

What's up with the way ruby alters variables this way?

Sorry about the vague question. I'm at a loss for words to describe this phenomenon, thus google wasn't much help. Please consider the following code:
array = [["name", "age"]]
a = []
x = ["Joe 32",
"Tom 45",
"Jim 36"]
x.each do |line|
name, age = line.split(/\s/)
a[0] = name
a[1] = age
array.push(a)
end
array.each do |x|
puts x.join(" ")
end
This produces:
name age
Jim 36
Jim 36
Jim 36
which is contrary to what I was expecting:
name age
Joe 32
Tom 45
Jim 36
Why is array affected after the fact by modifying a?
You want to set a to a new Array object inside the each. At the moment, you're pushing the same a object to the array, which is why it's returning the same value three times. Even better would be to not use a at all and instead convert the code into something like this:
x.each do |line|
name, age = line.split(/\s/)
array.push([name, age])
end
You could make it smaller than that even by moving the line.split to be within the push method, but I think that reduces readability and doesn't explain what information you're getting out of split.
This is slightly more advanced, but to build on Ryan's answer, rather than doing
x.each do |line|
name, age = line.split(/\s/)
array.push([name, age])
end
, you could use the map function, and have
people = x.map do |line|
name, age = line.split(/\s/)
[name, age]
end
desired_result = [["name", "age"]] + people
This is a slightly more "functional programming" approach. I'm sure this is a very rough summary, but in functional programming, you don't modify existing objects, you only create new objects instead.
As an aside, if you wanted to verify Ryan's answer, you could use object_id on each of the objects:
array.each_with_index do |object, index|
puts "Object #{index} (which is #{object.inspect}) has an object id of #{object.object_id}"
end
which gives
Object 0 (which is ["name", "age"]) has an object id of 10204144
Object 1 (which is ["Jim", "36"]) has an object id of 10248384
Object 2 (which is ["Jim", "36"]) has an object id of 10248384
Object 3 (which is ["Jim", "36"]) has an object id of 10248384

Help me refactor my World Cup Challenge Script

I am setting up a World Cup Challenge between some friends, and decided to practice my Ruby and write a small script to automate the process.
The Problem:
32 World Cup qualifiers split into 4 tiers by their Fifa ranking
8 entries
Each entry is assigned 1 random team per tier
Winner takes all :-)
I wrote something that suffices yet is admittedly brute force. But, in my attempt to improve my Ruby, I acknowlege that this code isn't the most elegant solution around - So I turn to you, the experts, to show me the way.
It may be more clear to check out this gist - https://gist.github.com/91e1f1c392bed8074531
My Current (poor) solution:
require 'yaml'
#teams = YAML::load(File.open('teams.yaml'))
#players = %w[Player1 Player2 Player3 Player4 Player5 Player6 Player7 Player8]
results = Hash.new
players = #players.sort_by{rand}
players.each_with_index do |p, i|
results[p] = Array[#teams['teir_one'][i]]
end
second = #players.sort_by{rand}
second.each_with_index do |p, i|
results[p] << #teams['teir_two'][i]
end
third = #players.sort_by{rand}
third.each_with_index do |p, i|
results[p] << #teams['teir_three'][i]
end
fourth = #players.sort_by{rand}
fourth.each_with_index do |p, i|
results[p] << #teams['teir_four'][i]
end
p results
I am sure there is a better way to iterate through the tiers, and duplicating the #players object ( dup() or clone() maybe?)
So from one Cup Fan to another, help me out.
I'm still learning Ruby too, but here's an attempt:
require 'yaml'
tiers = YAML::load(File.open('world_cup_tiers.yaml'))
players = %w[P1 P2 P3 P4 P5 P6 P7 P8]
draws = Hash.new { |h,k| h[k] = [] }
tiers.each do |tier, teams|
players.zip(teams.sort_by{rand}).each do |tuple|
player, team = tuple
draws[player].push(team)
end
end
players.each { |player| puts [player, draws[player]].join(' ') }

Resources