Issues with the output format of hash - arrays

So I was trying to create a program that resembles a grocery list where the user puts the item and its associated cost and it would display it as a form of a list. So I created this:
arr = []
arr2 = []
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
arr << item
arr2 << cost
end
h = { arr => arr2 }
for k,v in h
puts "#{k} costs #{v}"
end
(Code is probably very inefficient, but with my limited starter knowledge it's the best I can do)
So my problem is when I try more than two items the results would display like this (Let's say I used Banana and Kiwi for item and put a random number for their costs):
["Banana", "Kiwi"] costs [2.0, 3,0]
I, however, would like it to display like this:
Banana costs $2.00
Kiwi costs $3.00
I know it probably has to do something with this line:
h = { arr => arr2 }
But I just don't know what I can change about it. I already spend hours trying to figure out how it works so if anyone can give me a hint or help me out I would appreciate it! (Also my apologies for the vague title, didn't know better on how to describe it...)

yes, you are correct. Problem is with this line h = { arr => arr2 }. This line will create a hash like h = {["Banana", "Kiwi"] => [2.0, 3,0]}.
1) You can modify your code as below if you want to use two arrays.
(0...arr.length).each do |ind|
puts "#{arr[ind]} costs $#{arr2[ind]}"
end
2) Better, you can use a hash to store the item and it's cost and then iterate over it to show the results
hash = {}
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
hash[item] = cost
end
hash.each do |k,v|
puts "#{k} costs $#{v}"
end

You are storing the item names and their costs in 2 different arrays. So, if want to keep your storage structure like that only, you will need to modify the display of result as below:
arr.each_with_index do |item, i|
puts "#{item} costs #{arr2[i]}"
end
But a better approach would be to store all the data in 1 hash instead of 2 arrays.
items = {}
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
items[item] = cost
end
items.each do |item, cost|
puts "#{item} costs #{cost}"
end
Let me know if it helps.

Related

Is this the best way for the below needed Ruby application? + [Help in Arrays]

Please advise if the code is correct or there is a better version and also how to sort the Array in descending orders & the output to be like that:
A program that takes a user’s input, then builds a hash from that input. Each key in the hash will be a word from the user; each value will be the number of times that word occurs. For example, if our program gets the string “the rain in Spain falls mainly on the plain,”
print "What is the sentence that needs to be analysed? "
sentence = gets.chomp
#puts sentence
#Text to Array
my_array = sentence.split
#Create the Hash
word_frequency = Hash.new(0)
#Iterate through the given array in the built Hash
my_array.each { |word|
word_frequency[word] += 1
}
puts word_frequency
Needed Output to be if we run the code:
I love coding to the moon and love web coding
Love 2
coding 2
I 1
to 1
..... etc.
sentence = "Is this the best way for the below needed Ruby application?"
sentence.split.tally.sort_by { |_word, count| - count }.to_h
# => {"the"=>2, "Is"=>1, "this"=>1, "best"=>1, "way"=>1, "for"=>1, "below"=>1, "needed"=>1, "Ruby"=>1, "application?"=>1}
Firstly tokenize sentence to words array, than tally makes hash where key is word and value is count, than sort descending
You can also sort like this
sentence.split.tally.sort_by(&:last).reverse.to_h
Also you can use unified case, for example with String#upcase method and to avoid capturing punctuation it's possible to use String#scan method
sentence.upcase.scan(/[[:word:]-]+/).tally.sort_by(&:last).reverse.to_h
After many trails & #mechnicov I had reached the below and hope to hear reviews:
print 'What is the sentence that needs to be analysed? '
sentence = gets.chomp
# Convert Text to Array:
my_array = sentence.split
# Create the Hash called histogram
histogram = Hash.new(0) # zero added as a counter for iterate through word and add on it if exist
my_array.each do |word|
histogram[word] += 1
end
# Sort descending by values
sorted_histogram = histogram.sort_by { |_k, v| v }.reverse
#Print final output:
sorted_histogram.each do |_word, _count|
puts _word + ' ' + _count.to_s
end

How to compare letters in two strings Ruby

I am new to ruby and creating a hangman game. Soo far I have my code comparing the words to the correct word. But I want it to compare letters. So basically, if the secrect word is glue, the user enters G it would come inncorrect, but if the user enters glue it would be correct. I need it to compare letter by letter just like hangman.
Having a bit of trouble with that. I have attached my code below.
secret_word = []
puts "enter a word to be guessed"
secret_word = gets.chomp
guess_letters = []
guess = ""
guess_limit = 3
guess_count = 0
out_of_guesses = false
while guess != secret_word and !out_of_guesses
if guess_count < guess_limit
puts "enter your guess: "
guess = gets.chomp()
guess_letters << guess
guess_count +=1
puts "you have used these letters thus far #{guess_letters.join(", ")}"
else
out_of_guesses = true
end
end
if out_of_guesses
puts "you Lose, the word was #{secret_word}"
else
puts "you win"
end
I'm not sure which hangman rules you are using but here's a rough draft that allows three failed attempts and works with lowercase characters
def guess_word(word, tries)
if tries < 1
puts "You are hanged!"
elsif word.empty?
puts "You guessed it! You are saved from the gallows!"
else
print "Enter character: "
c = STDIN.getc.downcase
STDIN.getc # get rid of newline
if word.index(c).nil?
puts "Ooops, #{c} was wrong!"
guess_word(word, tries - 1)
else
puts "#{c} was correct!"
guess_word(word.sub(/["#{c}"]/, ''), tries)
end
end
end
if __FILE__ == $0
TRIES = 3
print "Enter word to guess: "
word = gets.chomp
guess_word(word.downcase, 3)
end
This is untested..
The rules of the game hangman are given at its Wiki. I've assumed the player trying to guess the word loses when all seven parts of the man on the gallows have been drawn (head, neck, left arm, body, right arm, left leg, right leg).
Helper methods
Draw the man being hanged
First create a hash that can be used to draw the partial or full hangman:
MAN = [" O\n", " |\n", "\\", "|", "/\n", " |\n/", " \\"].
map.each_with_object([""]) { |s,arr| arr << (arr.last + s) }.
each.with_index.with_object({}) { |(s,i),h| h[i] = s }
The keys are the number of incorrect guesses. For example:
puts MAN[2]
O
|
puts MAN[6]
O
|
\|/
|
/
Keep track of the positions of the letters of the word
Next create a hash whose keys are unique letters of the secret word and whose values are arrays of indices of the keys location(s) in the word.
def construct_unknown(word)
word.each_char.with_index.with_object({}) { |(c,i),h| (h[c] ||= []) << i }
end
For example,
unknown = construct_unknown("beetle")
#=> {"b"=>[0], "e"=>[1, 2, 5], "t"=>[3], "l"=>[4]}
We will also create an empty hash for letters whose positions are known:
known = {}
Move guessed letters from the hash unknown to the hash known
If a letter that is guessed is a key of unknown that key and value are moved to known.
def move_unknown_to_known(letter, unknown, known)
known.update(letter=>unknown[letter])
unknown.delete(letter)
end
For example (for unknown and known above),
move_unknown_to_known("e", unknown, known)
unknown #=> {"b"=>[0], "t"=>[3], "l"=>[4]}
known #=> {"e"=>[1, 2, 5]}
See if the guesser has won or lost
We to determine when, after guessing a letter, the player has won or lost, or is to continue:
def win?(word_size, known)
known.values.flatten.sum == word_size
end
def lose?(wrong_guess_count)
wrong_guess_count == HANGMAN.size
end
For example,
win?(word.size, known)
#=> false
lose?(6) #=> false
lose?(7) #=> true
Display the known letters
def display_known(word_size, known)
known.each_with_object('_' * word_size) { |(k,a),s| a.each { |i| s[i] = k } }
end
For example (recall word #=> "beetle"),
puts display_known(word.size, known)
_ee__e
Main method
We are now ready to write the main method.
def hangman
puts "Player 2, please avert your eyes for a moment."
print "Player 1: enter a secret word with at least two letters: "
word = gets.chomp.downcase
unknown = construct_unknown(word)
known = {}
wrong_guess_count = 0
loop do
puts display_known(word.size, known)
puts MAN[wrong_guess_count] if wrong_guess_count > 0
if win?(word.size, known)
puts "You win! You win! Congratulations!"
break
end
if lose?(wrong_guess_count)
puts "Sorry, but you've run out of guesses"
break
end
print "Player 2: enter a letter or your guess of the word: "
guess = gets.chomp.downcase
if guess.size > 1
if guess == word
puts word
puts "You win! You win! Congratulations!"
break
else
puts "Sorry, that's not the word"
wrong_guess_count += 1
end
elsif unknown.key?(guess)
nbr = unknown[guess].size
puts nbr == 1 ? "There is 1 #{guess}" : "There are #{nbr} #{guess}'s"
move_unknown_to_known(guess, unknown, known)
else
puts "Sorry, the word contains no #{guess}'s"
wrong_guess_count += 1
end
end
end
Example
After explaining the rules to the two players and to the audience, the guest host ends by saying, "And don't forget, when guessing a letter or the word it must be expressed as a question...one moment...hold that...I've been told it is not necessary to frame that as a question".
Suppose the word is beetle and the letter guesses are 't', 'i', 'a', 'l', 'r', 's', 't', 'u', 'e', 'beetle'.
hangman
Player 2, please avert your eyes for a moment.
Player 1: enter a secret word with at least two letters: beetle
______
Player 2: enter a letter or your guess of the word: t
There is 1 t
___t__
Player 2: enter a letter or your guess of the word: i
Sorry, the word contains no i's
___t__
O
Player 2: enter a letter or your guess of the word: a
Sorry, the word contains no a's
___t__
O
|
Player 2: enter a letter or your guess of the word: l
There is 1 l
___tl_
O
|
Player 2: enter a letter or your guess of the word: r
Sorry, the word contains no r's
___tl_
O
|
\
Player 2: enter a letter or your guess of the word: s
Sorry, the word contains no s's
___tl_
O
|
\|
Player 2: enter a letter or your guess of the word: t
Sorry, the word contains no t's
___tl_
O
|
\|/
Player 2: enter a letter or your guess of the word: u
Sorry, the word contains no u's
___tl_
O
|
\|/
|
/
Player 2: enter a letter or your guess of the word: e
There are 3 e's
_eetle
O
|
\|/
|
/
Player 2: enter a letter or your guess of the word: beetle
beetle
You win! You win! Congratulations!

How do I get the last elements of an array in Ruby?

How do I pull the values from an array like you do with .map? Here's my code:
counter = 0
ary = Array.new
puts "How many teams do you have to enter?"
hm = gets.to_i
until counter == hm do
puts "Team City"
city = gets.chomp
puts "Team Name"
team = gets.chomp
ary.push([city, team])
counter += 1
end
ary.map { |x, y|
puts "City: #{x} | Team: #{y}"
}
print "The last team entered was: "
ary.last
The end result looks like this:
City: Boston | Team: Bruins
City: Toronto | Team: Maple Leafs
The last team entered was:
=> ["Toronto", "Maple Leafs"]
But I want the last line to read
The last team entered was: Toronto Maple Leafs
How do I get my values in that line without the =>, brackets and quotes?
Basically, you question is “how to join string array elements into a single string,” and Array#join comes to the rescue:
["Toronto", "Maple Leafs"].join(' ')
#⇒ "Toronto Maple Leafs"
An alternative way with *:
puts ["Toronto", "Maple Leafs"] * ', '
#Toronto, Maple Leafs
#=> nil
But I don't think anyone uses this notation, so as recommended in another answer use join.
Use print instead of puts when you don't want a new line character at the end of the line for example when getting user input, furthermore you can also use #{variable} to print within the same line using puts:
counter = 0
ary = Array.new
print "How many teams do you have to enter? "
hm = gets.to_i
until counter == hm do
print "Team #{counter + 1} City: "
city = gets.chomp
print "Team #{counter + 1} Name: "
team = gets.chomp
ary.push([city, team])
counter += 1
end
ary.map { |x, y| puts "City: #{x} | Team: #{y}" }
puts "The last team entered was: #{ary.last.join(' ')}"
Example Usage:
How many teams do you have to enter? 2
Team 1 City: Boston
Team 1 Name: Bruins
Team 2 City: Toronto
Team 2 Name: Maple Leafs
City: Boston | Team: Bruins
City: Toronto | Team: Maple Leafs
The last team entered was: Toronto Maple Leafs
Try it here!
Try it:
team_last = ary.last
puts "The last team entered was:" + team_last[0] + team_last[1]
As per your code ary.last itself return an array so first you would need to convert it to a string by joining the two elements in the array by ary.last.join(' ') and then you will have to interpolate it with the your message string i.e "The last team entered was: #{ary.last.join(' ')}"
The last two lines of your code would change to :
print "The last team entered was: #{ary.last.join(' ')}"

Ruby loop and array

I've been trying to teach myself Ruby. I've found a few code problems to try solving, but I'm stuck. Here is what I have and the problems I'm trying to solve.
My algorithm is as follows:
Prompt the user to enter a number between 1 and 10 (inclusive).
Read that number into an appropriately named variable.
Test that the number lies in the appropriate range.
Input new number if it is out of bounds as per the condition.
Use the number entered by the user to create an array with that
number of elements.
Write a loop which will run through a number of iterations equal to
the size of the array.
Each time through, prompt the user to enter a text string - a name
(names of cars, that sort of thing).
Once the array is entered, display the contents of the array three
items to a line.
You will want a for loop for this, and within the for loop you should include a decision which will insert a line break at the appropriate places.
Also,
Separate the array elements with dashes - but do not put a dash
before the first element on a line, and do not put a dash after the
last element on a line.
Use a Ruby function to sort the array alphabetically, then display it
again, the same way as before.
Reverse the order of the array
Display its contents a third time, again putting three elements on each line of output and placing dashes the way you did with the first display effort.
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if s >0 && s <= 10
break
else
puts "Interger entered is outside specified range."
end
end
array=[]
array.size
loop do
print "Enter name of a car model: "
car=gets.chomp
array<<car
for i in array
array.slice(1..9) {|car|
puts car.join(", ")
}
end
end
Is that solution you looking for?
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if (1..10).include?(s)
arr = [""] * s
i = 0
while i < arr.length
print "Enter name of a car model: "
car = gets.chomp
arr[i] = car
i += 1
end
puts arr.join(", ")
break
else
puts "Interger entered is outside specified range."
break
end
end
Result is:
[retgoat#iMac-Roman ~/temp]$ ruby loop.rb
Enter an integer between 1 and 10: 2
Enter name of a car model: car_a
Enter name of a car model: car_b
car_a, car_b
UPDATE
Below solution to print an array by 3 elements per line with natural sorting
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if (1..10).include?(s)
arr = [""] * s
i = 0
while i < arr.length
print "Enter name of a car model: "
car = gets.chomp
arr[i] = car
i += 1
end
puts arr.sort.each_slice(3){ |e| puts "#{e.join(", ")}\n"}
break
else
puts "Interger entered is outside specified range."
break
end
end
Result is:
[retgoat#iMac-Roman ~/temp]$ ruby loop.rb
Enter an integer between 1 and 10: 4
Enter name of a car model: z
Enter name of a car model: a
Enter name of a car model: x
Enter name of a car model: b
a, b, x
z
And reverse sorting:
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if (1..10).include?(s)
arr = [""] * s
i = 0
while i < arr.length
print "Enter name of a car model: "
car = gets.chomp
arr[i] = car
i += 1
end
puts arr.sort{ |x, y| y <=> x }.each_slice(3){ |e| puts "#{e.join(", ")}\n"}
break
else
puts "Interger entered is outside specified range."
break
end
end
Result is:
[retgoat#iMac-Roman ~/temp]$ ruby loop.rb
Enter an integer between 1 and 10: 4
Enter name of a car model: z
Enter name of a car model: a
Enter name of a car model: x
Enter name of a car model: b
z, x, b
a
It's better to split you program into small pieces. Also, try not to use loop without necessity.
# Specify Exception class for your context
class ValidationException < RuntimeError
end
def number_of_cars_from_input
# Get user input
print 'Enter an integer between 1 and 10: '
number = gets.chomp.to_i
# Validate input for your requirements
unless (1..10).cover?(number)
raise ValidationException, 'Interger entered is outside specified range.'
end
number
rescue ValidationException => err
# Print exception and retry current method
puts err
retry
end
# Get car name from user input
def car_from_input
print 'Enter name of a car model: '
gets.chomp
end
# Create array with size equal to number from imput and fill it with cars
array_of_cars = Array.new(number_of_cars_from_input) { car_from_input }
# Separate cars in groups by 3 and join groups
puts array_of_cars.each_slice(3).map { |a| a.join(', ') }

How to check if a variable in one array exists in another and how to replace it in ruby?

I'm asking for input and then asking which words does the user want to redact. Then trying to print out the string with the words redacted.
puts "Input please"
text = gets.chomp
puts "What would you like redacted, sir? Type DONE when nothing else to redact"
redact = Array.new
answer = gets.chomp
until answer.downcase == "done"
redact << answer.downcase
answer = gets.chomp
end
words = text.split (" ")
words.each do |word|
if word.include? (# An array to redact)
# Replace words here
print "REDACTED "
else
print word + " "
end
end
Just solved it one way!!
words.each do |word|
if redact.include?(word)
#IF THE ARRAY REDACT INCLUDES VARIABLE WORD, PRINT REDACT
print "REDACTED "
else print word + " "
end
end
You can do something like this.
words.map {|word| redact.include?(word) ? 'REDACTED' : word}
This will iterate over each of the elements in your words array, and look to see if the element is in the redact array. If it is, it changes its value to 'REDACT', else it keeps the word the same.
Note that this will create a new array, so you will probably want to assign it to a new variable. If you want to edit the words array in place, you can use the map! function.
boolean ? true_path : false_path
is called a ternary, it is just short for
if boolean
true_path
else
false_path
end

Resources