Ruby Loops While Statements - ruby

I have an array of strings dictionary and a string target:
dictionary = ['a', 'b', 'c', 'ab', 'abc']
target = 'abba'
My goal is to return combinations of words from dictionary that can make up target. It should return something like this
['a abc', 'a a b c', 'a ab c']
This is what I have:
def possible_combinations(dictionary, target)
results = [] #eventually an array of results
i = 0 #to go through the dictionary index starting at 0
t = 0 #to go through the target index starting at 0
while i < dictionary.count #while 0 is less than the total index in dict
while t < target.length
if dictionary[i] == target[t]#dict is not changing but target[t] is changing
puts 'I am ' + dictionary[i] + ' at DICT for now'
puts 'I am ' + target[t] + ' at t for now'
puts 'I match somewhere in target so I am added.'#dict[1] is not happening here.
# results.push(dictionary[i])
if results.empty?
results.push(dictionary[i])
puts results
else
results = results[0] + ' ' + dictionary[i] #this is not entirely working?
puts results
end
else
puts 'forget about me'
end
t = t + 1
end
i = i + 1
end
end
and when I run it, I get this:
I am a at DICT for now
I am a at t for now
I match somewhere in target so I am added.
a
forget about me
forget about me
I am a at DICT for now
I am a at t for now
I match somewhere in target so I am added.
a a
I notice that target[t] is changing, but dictionary[i] is not. I don't understand nested while loops. I think the inner while loop has to finish before it heads to the outer, so dictionary[i] is getting stuck. I want to iterate over i for both dictionary and target, so I am using nested while loops.
If target = 'aaaba', I get this:
I am a at DICT for now
I am a at t for now
I match somewhere in target so I am added.
a
I am a at DICT for now
I am a at t for now
I match somewhere in target so I am added.
a a
I am a at DICT for now
I am a at t for now
I match somewhere in target so I am added.
a a
forget about me
I am a at DICT for now
I am a at t for now
I match somewhere in target so I am added.
a a
Notice how the results got stuck with two 'a' but not three or four?

Instead of using while you can use each on the dictionary and each_char on the target
dictionary.each do |word|
target.each_char do |char|
puts word, char
end
end
The problem w/ your current loops is that you are initializing t = 0 outside both loops, so you only loop through the target once before the inner while condition is always false. If you move that declaration inside the first while loop you will get a result more similar to what you expect

ri Array.each
ri String.index
You're doing this in the most un-ruby like way possible. Read the chapter on the Enumerable module. Look up all the methods String supports. There is almost always a better way than using while in Ruby.
http://ruby-doc.com/docs/ProgrammingRuby/
Note that while String.[1] works Strings are not enumerable, you'd be better off to split the string into an Array of chars if you want to enumerate over it. Or better yet use string search functions rather than direct comparisons. In your
code
dictionary[i] is a string
while
target[i] is a single char.
So you test will never be equal when the dictionary elements are longer than one char.

Here is a more Ruby-like way to write your program:
def possible_combinations(dictionary, target)
results = #eventually an array of results
dictionary.each do |d|
str = ''
target.each do |tl
if d == t #dict is not changing but target[t] is changing
puts 'I am ' + d + ' at DICT for now'
puts 'I am at target ' + t + ' now'
puts 'I match somewhere in target so I am added.' #dict[1] is not happening here.
str = << ' ' unless str.empty?
str << d
puts results
else
puts 'forget about me'
end
end
results << str
end
end
It took just a couple minutes to do this translation. I mainly removed the indices, so the iterations are over elements of the dictionary and target objects.

Related

Making a sorted array of user's input

I'm learning Ruby with 'Learn to Program' by Chris Pine. On chapter 10 I should write a program where the user types as many words as he like and when he's done, he can just press Enter on an empty line and exit.
I came up with this:
puts "Type whatever you want!"
index = 0
word = ''
array = []
while word != nil
word << gets.chomp
array[index] = word
index = index + 1
end
puts ''
puts array.sort
But that doesn't work. What did I miss? Is there another way I could define word without having to repeat it?
The word will not have nil value. It will be an empty string. So you need to check for that:
while word != ""
# or even better
while !word.empty?
Also, you are adding everything to your word. You probably want to assign to it instead:
word = gets.chomp
Per author's comment:
begin
# your code here
end while !word.empty?
# OR more readable
begin
# your code here
end until word.empty?
It seems like there's a simpler solution, if I'm reading the question correctly.
You could do something like this:
user_input = gets.chomp.split(" ").sort
ex)
input: bananas clementine zebra tree house plane mine
output: ["bananas", "clementine", "house", "mine", "plane", "tree", "zebra"]
Here's a simple loop that you could do just for kicks:
arr = []
arr << $_.strip until gets =~ /^\s*$/
puts arr.sort
$_ is a special variable that evaluates to the last input read from STDIN. So basically this reads "Call gets and check if the input is just spaces. If it is then break out of the loop, otherwise append the last input with whitespace removed value onto the array and continue looping."
Or even more fun, a one liner:
puts [].tap {|arr| arr << $_.strip until gets =~ /^\s*$/}.sort
Basically same thing as above except using tap to initialize the variable.
To answer your questions:
Is there another way I could define word without having to repeat it?
Use side effects of assignment. In ruby when you assign a variable the return value of that assignment is the assigned variable, as in:
irb(main):001:0> (variable = 2) == 2
=> true
The idea would be to put the assignment in the your conditional. If I were to write something like this in a comprehensible loop, as opposed to those above, I'd write something like this:
arr = []
while !(word = gets.strip).empty?
arr << word
end
puts arr.sort
Using loop might simplify the code:
a = []
loop do
input = gets.chomp
if input.empty?
break
else
a << input
end
end
a.sort!
puts a

Reversing a Ruby String, without .reverse method

I am working on this coding challenge, and I have found that I am stuck. I thought it was possible to call the .string method on an argument that was passed in, but now I'm not sure. Everything I've found in the Ruby documentation suggests otherwise. I'd really like to figure this out without looking at the solution. Can someone help give me a push in the right direction?
# Write a method that will take a string as input, and return a new
# string with the same letters in reverse order.
# Don't use String's reverse method; that would be too simple.
# Difficulty: easy.
def reverse(string)
string_array = []
string.split()
string_array.push(string)
string_array.sort! { |x,y| y <=> x}
end
# These are tests to check that your code is working. After writing
# your solution, they should all print true.
puts(
'reverse("abc") == "cba": ' + (reverse("abc") == "cba").to_s
)
puts(
'reverse("a") == "a": ' + (reverse("a") == "a").to_s
)
puts(
'reverse("") == "": ' + (reverse("") == "").to_s
)
This is the simplest one line solution, for reversing a string without using #reverse, that I have come across -
"string".chars.reduce { |x, y| y + x } # => "gnirts"
Additionally, I have never heard of the #string method, I think you might try #to_s.
Easiest way to reverse a string
s = "chetan barawkar"
b = s.length - 1
while b >= 0
print s[b]
b=b-1
end
You need to stop the search for alternative or clever methods, such as altering things so you can .sort them. It is over-thinking the problem, or in some ways avoiding thinking about the core problem you have been asked to solve.
What this test is trying to get you you to do, is understand the internals of a String, and maybe get an appreciation of how String#reverse might be implemented using the most basic string operations.
One of the most basic String operations is to get a specific character from the string. You can get the first character by calling string[0], and in general you can get the nth character (zero-indexed) by calling string[n].
In addition you can combine or build longer strings by adding them together, e.g. if you had a="hell" and b="o", then c = a + b would store "hello" in the variable c.
Using this knowledge, find a way to loop through the original string and use that to build the reverse string, one character at a time. You may also need to look up how to get the length of a string (another basic string method, which you will find in any language's string library), and how to loop through numbers in sequence.
You're on the right track converting it to an array.
def reverse(str)
str.chars.sort_by.with_index { |_, i| -i }.join
end
Here is a solution I used to reverse a string without using .reverse method :
#string = "abcde"
#l = #string.length
#string_reversed = ""
i = #l-1
while i >=0 do
#string_reversed << #string[i]
i = i-1
end
return #string_reversed
Lol, I am going through the same challenge. It may not be the elegant solution, but it works and easy to understand:
puts("Write is a string that you want to print in reverse")
#taking a string from the user
string = gets.to_s #getting input and converting into string
def reverse(string)
i = 0
abc = [] # creating empty array
while i < string.length
abc.unshift(string[i]) #populating empty array in reverse
i = i + 1
end
return abc.join
end
puts ("In reverse: " + reverse(string))
Thought i'd contribute my rookie version.
def string_reverse(string)
new_array = []
formatted_string = string.chars
new_array << formatted_string.pop until formatted_string.empty?
new_array.join
end
def reverse_str(string)
# split a string to create an array
string_arr = string.split('')
result_arr = []
i = string_arr.length - 1
# run the loop in reverse
while i >=0
result_arr.push(string_arr[i])
i -= 1
end
# join the reverse array and return as a string
result_arr.join
end

Capitalizing words in an array, Ruby

I'm going through App Academy's Ruby Prep questions, and I want to know why this solution works. It appears that the words array is never altered and yet the method works. Is this a glitch in the matrix, or is it right under my nose?
def capitalize_words(string)
words = string.split(" ")
idx = 0
while idx < words.length
word = words[idx]
word[0] = word[0].upcase
idx += 1
end
return words.join(" ")
end
The method works because word contains a reference to the array position. So when you assign:
word = words[idx]
You're just using word as a shorthand to operate on that array element, which gets modified by:
word[0] = word[0].upcase
--
Also, if you'd like to come back to this answer after learning some Ruby, here's a simplified version of the method:
def capitalize_words(string)
string.split.map(&:capitalize).join(' ')
end
String#[]= is a mutating operation. To illustrate using a concise, contained excerpt from your code:
word = "foo"
word[0] = word[0].upcase # <-- verbatim from your code
word #=> "Foo"
word is still the same exact object contained in the array words (arrays simply contain references to objects, not the data within them), but it has been mutated in-place. It’s generally best to avoid mutations whenever possible as it makes it non-obvious what is happening (as you can see).
Your code could also be more concisely written using map & capitalize (and without any mutations):
string.split(' ').map(&:capitalize).join(' ')
word = word[idx] creates a copy of your data. It will then modify that copy instead of the words in the original array.
Simple solution would be:
def capitalize_words(string)
words = string.split(" ")
idx = 0
while idx < words.length
words[idx][0] = words[idx][0].upcase
idx += 1
end
return words.join(" ")
end

Simple Ruby Input Scraper

I'm completely new to ruby and wanted to ask for some help with this ruby script.
it's supposed to take in a string and find out which character occurs the most frequently. It does this using a hash, it stores all the characters in a hash and then iterates through it to find the one with greatest value. As of right now it doesn't seem to be working properly and i'm not sure why. It reads the characters in properly as far as i can tell with print statements. Any help is appreciated.
Thanks!
puts "Enter the string you want to search "
input = gets.chomp
charHash = Hash.new
input.split("").each do |i|
if charHash.has_key?(i)
puts "incrementing"
charHash[i]+=1
else
puts"storing"
charHash.store(i, 1)
end
end
goc = ""
max = 0
charHash.each { |key,value| goc = key if value > max }
puts "The character #{goc} occurs the most frequently"
There are two major issues with you code:
As commented by Holger Just, you have to use += 1 instead of ++
charHash.store(:i, 1) stores the symbol :i, you want to store i
Fixing these results in a working code (I'm using snake_case here):
char_hash = Hash.new
input.split("").each do |i|
if char_hash.has_key?(i)
char_hash[i] += 1
else
char_hash.store(i, 1)
end
end
You can omit the condition by using 0 as your default hash value and you can replace split("").each with each_char:
char_hash = Hash.new(0)
input.each_char do |i|
char_hash[i] += 1
end
Finally, you can pass the hash into the loop using Enumerator#with_object:
char_hash = input.each_char.with_object(Hash.new(0)) { |i, h| h[i] += 1 }
I might be missing something but it seems that instead of
charHash.each { |key,value| goc = key if value > max }
you need something like
charHash.each do |key,value|
if value > max then
max = value
goc = key
end
end
Notice the max = value statement. In your current implementation (i.e. without updating the max variable), every character that appears in the text at least once satisfies the condition and you end up getting the last one.

Storing output into a variable to be used in an array

A snippet of my code below flips a coin and outputs a result of 10 total heads or tails.
(e.g. Heads Tails Heads Tails...)
I'd like to store this into a variable where I can put it into an array and use its strings.
%w[act] only outputs the string "act". How can I get that line of code to output my array of strings from the line act = coin.flip?
Updated and added full code
class Coin
def flip
flip = 1 + rand(2)
if flip == 2
then puts "Heads"
else
puts "Tails"
end
end
end
array = []
10.times do
coin = Coin.new
array << coin.flip
end
puts array
This:
10.times do
coin = Coin.new
act = coin.flip
end
doesn't produce an array. It simply creates ten coin flips and throws them all away, the result of that expression is, in fact, 10. If you want an array, you'll need to build one.
You could take Douglas's approach or try something a bit more idiomatic.
The Integer#times method returns an enumerator so you can use any of the Enumerable methods on it rather than directly handing it a block. In particular, you could use collect to build an array in one nice short piece of code:
a = 10.times.collect { Coin.new.flip }
That gives you 10 flips in the Array a and then you can puts a or puts a.join(', ') or whatever you want.
The %w[] won't work because that's for generating an Array of whitespace separated words:
%w[] Non-interpolated Array of words, separated by whitespace
So %w[a b c] is just a nicer way of saying ['a', 'b', 'c'] and the words within %w[] are treated as single quoted strings rather than variables or method calls to be evaluated.
Seems that there is some editing going on. You'll also want to modify your flip method to return the flip rather than print it:
def flip
flip = 1 + rand(2)
if flip == 2
"Heads"
else
"Tails"
end
end
Then you'll get your Heads and Rails in the array.
Put the act results into an array.
arr = []
10.times do
coin = Coin.new
arr << coin.flip
end
p arr # => [...]

Resources