Trying to find vowels of a string using Ruby while loops - ruby

def count_vowels(string)
vowels = ["a", "e", "i", "o", "u"]
i = 0
j = 0
count = 0
while i < string.length do
while j < vowels.length do
if string[i] == vowels[j]
count += 1
break
end
j += 1
end
i += 1
end
puts count
end
I'm having trouble spotting where this goes wrong. If this program encounters a consonant, it stops. Also, how would the same problem be solved using the ".each" method?

The problem is that you never reset j to zero.
The first time your outer while loop runs, which is to compare the first character of string to each vowel, j is incremented from 0 (for "a") to 4 (for "u"). The second time the outer loop runs, however, j is already 4, which means it then gets incremented to 5, 6, 7 and on and on. vowels[5], vowels[6], etc. all evaluate to nil, so characters after the first are never counted as vowels.
If you move the j = 0 line inside the outer while loop, your method works correctly.
Your second question, about .each, shows that you're already thinking along the right lines. while is rarely seen in Ruby and .each would definitely be an improvement. As it turns out, you can't call .each on a String (because the String class doesn't include Enumerable), so you have to turn it into an Array of characters first with the String#chars method. With that, your code would look like this:
def count_vowels(string)
chars = string.chars
vowels = ["a", "e", "i", "o", "u"]
count = 0
chars.each do |char|
vowels.each do |vowel|
if char == vowel
count += 1
break
end
end
end
puts count
end
In Ruby, though, we have much better ways to do this sort of thing. One that fits particularly well here is Array#count. It takes a block and evaluates it for each item in the array, then returns the number of items for which the block returned true. Using it we could write a method like this:
def count_vowels(string)
chars = string.chars
vowels = ["a", "e", "i", "o", "u"]
count = chars.count do |char|
is_vowel = false
vowels.each do |vowel|
if char == vowel
is_vowel = true
break
end
end
is_vowel
end
puts count
end
That's not much shorter, though. Another great method we can use is Enumerable#any?. It evaluates the given block for each item in the array and returns true upon finding any item for which the block returns true. Using it makes our code super short, but still readable:
def count_vowels(string)
chars = string.chars
vowels = %w[ a e i o u ]
count = chars.count do |char|
vowels.any? {|vowel| char == vowel }
end
puts count
end
(Here you'll see I threw in another common Ruby idiom, the "percent literal" notation for creating an array: %w[ a e i o u ]. It's a common way to create an array of strings without all of those quotation marks and commas. You can read more about it here.)
Another way to do the same thing would be to use Enumerable#include?, which returns true if the array contains the given item:
def count_vowels(string)
vowels = %w[ a e i o u ]
puts string.chars.count {|char| vowels.include?(char) }
end
...but as it turns out, String has an include? method, too, so we can do this instead:
def count_vowels(string)
puts string.chars.count {|char| "aeiou".include?(char) }
end
Not bad! But I've saved the best for last. Ruby has a great method called String#count:
def count_vowels(string)
puts string.count("aeiou")
end

Related

Having trouble adding new elements to my hash (Ruby)

new to Ruby, new to coding in general...
I'm trying to add new elements into my hash, incrementing the value when necessary. So I used Hash.new(0) and I'm trying to add new values using the "+=" symbol, but when I do this I get an error message -
"/tmp/file.rb:6:in `+': String can't be coerced into Integer (TypeError)
from /tmp/file.rb:6:in `block in stockList'
from /tmp/file.rb:3:in `each'
from /tmp/file.rb:3:in `each_with_index'
from /tmp/file.rb:3:in `stockList'
from /tmp/file.rb:24:in `<main>'
"
Here's my code:
def stockList(stock, cat)
hash = Hash.new(0)
stock.each_with_index do |word, i|
if cat.include?(word[i])
char = word[i]
hash[char] += num(word)
end
end
new_arr = []
hash.each do |k, v|
new_arr.push(k,v)
end
return new_arr
end
def num(word)
nums = "1234567890"
word.each_char.with_index do |char, i|
if nums.include?(char)
return word[i..-1]
end
end
end
puts stockList(["ABAR 200", "CDXE 500", "BKWR 250", "BTSQ 890", "DRTY 600"], ["A", "B"])
Does anyone know why this is happening?
It's a codewars challenge -- I'm basically given two arrays and am meant to return a string that adds the numbers associated with the word that starts with the letter(s) listed in the second array.
For this input I'm meant to return " (A : 200) - (B : 1140) "
Your immediate problem is that num(word) returns a string, and a string can't be added to a number in the line hash[char] += num(word). You can convert the string representation of a numeric value using .to_i or .to_f, as appropriate for the problem.
For the overall problem I think you've added too much complexity. The structure of the problem is:
Create a storage object to tally up the results.
For each string containing a stock and its associated numeric value (price? quantity?), split the string into its two tokens.
If the first character of the stock name is one of the target values,
update the corresponding tally. This will require conversion from string to integer.
Return the final tallies.
One minor improvement is to use a Set for the target values. That reduces the work for checking inclusion from O(number of targets) to O(1). With only two targets, the improvement is negligible, but would be useful if the list of stocks and targets increase beyond small test-case problems.
I've done some renaming to hopefully make things clearer by being more descriptive. Without further ado, here it is in Ruby:
require 'set'
def get_tallies(stocks, prefixes)
targets = Set.new(prefixes) # to speed up .include? check below
tally = Hash.new(0)
stocks.each do |line|
name, amount = line.split(/ +/) # one or more spaces is token delimiter
tally[name[0]] += amount.to_i if targets.include?(name[0]) # note conversion to int
end
tally
end
stock_list = ["ABAR 200", "CDXE 500", "BKWR 250", "BTSQ 890", "DRTY 600"]
prefixes = ["A", "B"]
p get_tallies(stock_list, prefixes)
which prints
{"A"=>200, "B"=>1140}
but that can be formatted however you like.
The particular issue triggering this error is that your def num(word) is essentially a no-op, returning the word without any change.
But you actually don't need this function: this...
word.delete('^0-9').to_i
... gives you back the word with all non-digit characters stripped, cast to integer.
Note that without to_i you'll still receive the "String can't be coerced into Integer" error: Ruby is not as forgiving as JavaScript, and tries to protect you from results that might surprise you.
It's a codewars challenge -- I'm basically given two arrays and am
meant to return a string that adds the numbers associated with the
word that starts with the letter(s) listed in the second array.
For this input I'm meant to return " (A : 200) - (B : 1140) "
This is one way to get there:
def stockList(stock, cat)
hash = Hash.new(0)
stock.each do |word|
letter = word[0]
if cat.include?(letter)
hash[letter] += word.delete('^0-9').to_i
end
end
hash.map { |k, v| "#{k}: #{v}" }
end
Besides type casting, there's another difference here: always choosing the initial letter of the word. With your code...
stock.each_with_index do |word, i|
if cat.include?(word[i])
char = word[i]
... you actually took the 1st letter of the 1st ticker, the 2nd letter of the 2nd ticker and so on. Don't use indexes unless your results depend on them.
stock = ["ABAR 200", "CDXE 500", "BKWR 250", "BTSQ 890", "DRTY 600"]
cat = ["A", "B"]
I concur with your decision to create a hash h with the form of Hash::new that takes an argument (the "default value") which h[k] returns when h does not have a key k. As a first step we can write:
h = stock.each_with_object(Hash.new(0)) { |s,h| h[s[0]] += s[/\d+/].to_i }
#=> {"A"=>200, "C"=>500, "B"=>1140, "D"=>600}
Then Hash#slice can be used to extract the desired key-value pairs:
h = h.slice(*cat)
#=> {"A"=>200, "B"=>1140}
At this point you have all the information you need to display the result any way you like. For example,
" " << h.map { |k,v| "(#{k} : #{v})" }.join(" - ") << " "
#=> " (A : 200) - (B : 1140) "
If h before h.slice(*cat) is large relative to h.slice(*cat) you can reduce memory requirements and probably speed things somewhat by writing the following.
require 'set'
cat_set = cat.to_set
#=> #<Set: {"A", "B"}>
h = stock.each_with_object(Hash.new(0)) do |s,h|
h[s[0]] += s[/\d+/].to_i if cat_set.include?(s[0])
end
#=> {"A"=>200, "B"=>1140}

Why does is the index of the array repeatedly be printed out to be 2?

I'm working on this function:
It's supposed to take in an array and match it with a given word to see if that word can be formed with the given array of strings.
I added the two commented lines because I wanted to see how the for-loop works.
def canformword(arr,word)
arrword = word.chars
arrleft = arr
flag = true
for i in 0...arrword.size
ch = arrword[i]
# puts arrword[i]
if !arrleft.include?(ch)
flag = false
break
else
ind = arrleft.index(ch)
# puts ind
arrleft.delete_at(ind)
end
end
if flag
puts 'can form word'
else
puts 'can not form word'
end
end
canformword(['y','b','z','e','a','u','t'], 'beauty')
canformword(['r','o','u','g','h'], 'tough')
When I uncomment those two lines, the following is the output:
Why does the output print out the index 2 repeatedly? I would think that it would print out the index of each letter in my arrleft array rather than repeatedly spitting out 2!
I understand the 1 it prints out, because that's the index of b, but the rest is weird to me.
b
1
e
2
a
2
u
2
t
2
y
0
can form word
t
can not form word
hear a better implementation that
def can_form_word?(chars_array, word)
(word.chars - chars_array).empty?
end
that's all.
here another implementation the Ruby way. Because your code is like C. I've been writing Ruby code for more than three years now, and I never used for loops.
def canformword(chars,word)
word.each_char do |char|
puts char
if !chars.include?(char)
return false # or puts "Can't form word"
end
end
true # or puts "Can form word"
end
this is because you are deleting the character at position ind(arrleft.delete_at(ind)); so each time array characters are shifting one cell left.
Now as all your letters 'e','a','u','t','y' are placed ordered way so it is showing 2,2,2,2 continuously.
Now look at 'y'; it is at position 0 ; so 0 is printed at end.
So the issue is because you are deleting the characters at position 'ind'.
So, to achieve this you can just do one thing ; do not delete the characters when found rather replace it by some numeric value like '0'.
You obtain 2 several times because you are deleting elements from the array. In that case you delete the second element every time so the next character, in the next iteration, take the index 2 again.
Problem
If you want do delete index 2 and 3 from an array, you need to delete them in decreasing order, becausing deleting index 2 would modify index of 3:
array = %w(a b c d e)
array.delete_at(3)
array.delete_at(2)
p array
#=> ["a", "b", "e"]
or
array = %w(a b c d e)
array.delete_at(2)
array.delete_at(2)
p array
#=> ["a", "b", "e"]
Solution
For your code, you just need to replace
arrleft.delete_at(ind)
with
arrleft[ind] = nil
Alternative
Since you take the numbers of characters into account, here's a modified version of a previous answer :
class Array
def count_by
each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
end
def subset_of?(superset)
superset_counts = superset.count_by
count_by.all? { |k, count| superset_counts[k] >= count }
end
end
def can_form_word?(chars, word)
word.chars.subset_of?(chars)
end
p can_form_word?(['y','b','z','e','a','u','t'], 'beauty')
#=> true
p can_form_word?(['y','b','z','e','u','t'], 'beauty')
#=> false
p can_form_word?(['a', 'c', 'e', 'p', 't', 'b', 'l'], 'acceptable')
#=> false
p ('acceptable'.chars - ['a', 'c', 'e', 'p', 't', 'b', 'l']).empty?
#=> true

Ruby method to reverse string input

I don't get why reversed_string=string[i] + reversed_string puts the last char first. It seems that string[i] would index the first char and not the last. So if the string was "abc" index 0 would be 'a' and not 'c'. Could someone please explain how ruby gets 'c' from index 0? And then, of course, 'b' from index 1? Etc, etc.
Write a method that will take a string as input, and return a new string with the same letters in reverse order.
Difficulty: easy.
def reverse(string)
reversed_string = ""
i = 0
while i < string.length
reversed_string = string[i] + reversed_string
i += 1
end
return reversed_string
end
puts("reverse(\"abc\") == \"cba\": #{reverse("abc") == "cba"}")
puts("reverse(\"a\") == \"a\": #{reverse("a") == "a"}")
puts("reverse(\"\") == \"\": #{reverse("") == ""}")
reversed_string = string[i] + reversed_string
For example, if string is "abc", string[0] is indeed "a", but here it's being put in the beginning of reversed_string, not the end. reversed_string is added up in each iteration as:
"a" + "" #string[0] + "" => "a"
"b" + "a" #string[1] + "a" => "ba"
"c" + "ba" #string[2] + "ba"=> "cba"
Assuming you can't use Ruby Class String's built in Reverse method, you could try the following
def reverse_string(string)
new_string = []
i = string.length-1
while i >= 0
new_string.push(string[i])
i -= 1
end
new_string.join
end
This will create a new string object, but it will reverse the string without using any built-in methods.
As you know, there is a method String#reverse to reverse a string. I understand you are not to use that method, but instead write your own, where the method's argument is the string to be reversed. Others will suggest ways you might do that.
As you are new to Ruby, I thought it might be instructive to show you how you could write a new method for the String class, say, String#my_reverse, that behaves exactly the same as String#reverse. Then for the string "three blind mice", we would have:
"three blind mice".reverse #=> "ecim dnilb eerht"
"three blind mice".my_reverse #=> "ecim dnilb eerht"
To create a method without arguments for the String class, we normally do it like this:
class String
def my_method
...
end
end
We invoke my_method by sending it a receiver that is an instance of the String class. For example, if write:
"three blind mice".my_method
we are sending the method String#my_method to the receiver "three blind mice". Within the definition of the method, the receiver is referred to as self. Here self would be "three blind mice". Similarly, just as the second character (at offset 1) of that string is "three blind mice"[1] #=> "h", self[1] #=> "h". We can check that:
class String
def my_method
puts "I is '#{self}'"
(0...self.size).each { |i| puts self[i] }
end
end
"three blind mice".my_method
would print:
I is 'three blind mice'
t
h
r
e
e
b
...
c
e
The method my_reverse is almost the same:
class String
def my_reverse
sz = self.size
str = ''
(0...sz).each { |i| str << self[sz-1-i] }
str
end
end
"three blind mice".my_reverse
#=> "ecim dnilb eerht"
You can think of self as a variable whose value is the receiver, but unlike a variable, you cannot reassign self to a different object. For example, we can write x = 1; x = 'cat', but we cannot write self = 'cat'. As we have already seen, however, we can change the references self makes to other objects, such as self[1] = 'r'.

frequency of a letter in a string

When trying to find the frequency of letters in 'fantastic' I am having trouble understanding the given solution:
def letter_count(str)
counts = {}
str.each_char do |char|
next if char == " "
counts[char] = 0 unless counts.include?(char)
counts[char] += 1
end
counts
end
I tried deconstructing it and when I created the following piece of code I expected it to do the exact same thing. However it gives me a different result.
blah = {}
x = 'fantastic'
x.each_char do |char|
next if char == " "
blah[char] = 0
unless
blah.include?(char)
blah[char] += 1
end
blah
end
The first piece of code gives me the following
puts letter_count('fantastic')
>
{"f"=>1, "a"=>2, "n"=>1, "t"=>2, "s"=>1, "i"=>1, "c"=>1}
Why does the second piece of code give me
puts blah
>
{"f"=>0, "a"=>0, "n"=>0, "t"=>0, "s"=>0, "i"=>0, "c"=>0}
Can someone break down the pieces of code and tell me what the underlying difference is. I think once I understand this I'll be able to really understand the first piece of code. Additionally if you want to explain a bit about the first piece of code to help me out that'd be great as well.
You can't split this line...
counts[char] = 0 unless counts.include?(char)
... over multiple line the way you did it. The trailing conditional only works on a single line.
If you wanted to split it over multiple lines you would have to convert to traditional if / end (in this case unless / end) format.
unless counts.include?(char)
counts[char] = 0
end
Here's the explanation of the code...
# we define a method letter_count that accepts one argument str
def letter_count(str)
# we create an empty hash
counts = {}
# we loop through all the characters in the string... we will refer to each character as char
str.each_char do |char|
# we skip blank characters (we go and process the next character)
next if char == " "
# if there is no hash entry for the current character we initialis the
# count for that character to zero
counts[char] = 0 unless counts.include?(char)
# we increase the count for the current character by 1
counts[char] += 1
# we end the each_char loop
end
# we make sure the hash of counts is returned at the end of this method
counts
# end of the method
end
Now that #Steve has answered your question and you have accepted his answer, perhaps I can suggest another way to count the letters. This is just one of many approaches that could be taken.
Code
def letter_count(str)
str.downcase.each_char.with_object({}) { |c,h|
(h[c] = h.fetch(c,0) + 1) if c =~ /[a-z]/ }
end
Example
letter_count('Fantastic')
#=> {"f"=>1, "a"=>2, "n"=>1, "t"=>2, "s"=>1, "i"=>1, "c"=>1}
Explanation
Here is what's happening.
str = 'Fantastic'
We use String#downcase so that, for example, 'f' and 'F' are treated as the same character for purposes of counting. (If you don't want that, simply remove .downcase.) Let
s = str.downcase #=> "fantastic"
In
s.each_char.with_object({}) { |c,h| (h[c] = h.fetch(c,0) + 1) c =~ /[a-z]/ }
the enumerator String#each_char is chained to Enumerator#with_index. This creates a compound enumerator:
enum = s.each_char.with_object({})
#=> #<Enumerator: #<Enumerator: "fantastic":each_char>:with_object({})>
We can view what the enumerator will pass to the block by converting it to an array:
enum.to_a
#=> [["f", {}], ["a", {}], ["n", {}], ["t", {}], ["a", {}],
# ["s", {}], ["t", {}], ["i", {}], ["c", {}]]
(Actually, it only passes an empty hash with 'f'; thereafter it passes the updated value of the hash.) The enumerator with_object creates an empty hash denoted by the block variable h.
The first element enum passes to the block is the string 'f'. The block variable c is assigned that value, so the expression in the block:
(h[c] = h.fetch(c,0) + 1) if c =~ /[a-z]/
evaluates to:
(h['f'] = h.fetch('f',0) + 1) if 'f' =~ /[a-z]/
Now
c =~ /[a-z]/
is true if and only if c is a lowercase letter. Here
'f' =~ /[a-z]/ #=> true
so we evaluate the expression
h[c] = h.fetch(c,0) + 1
h.fetch(c,0) returns h[c] if h has a key c; else it returns the value of Hash#fetch's second parameter, which here is zero. (fetch can also take a block.)
Since h is now empty, it becomes
h['f'] = 0 + 1 #=> 1
The enumerator each_char then passes 'a', 'n' and 't' to the block, resulting in the hash becoming
h = {'f'=>1, 'a'=>1, 'n'=>1, 't'=>1 }
The next character passed in is a second 'a'. As h already has a key 'a',
h[c] = h.fetch(c,0) + 1
evaluates to
h['a'] = h['a'] + 1 #=> 1 + 1 => 2
The remainder of the string is processed the same way.

How does this function work?

The ordered_vowel_words method and ordered_vowel_word? helper method accept a word and return the word back if the vowels of the word are in the order of (a,e,i,o,u).
I'm having trouble understanding the logic. Particularly how the last block (0...(vowels_arr.length - 1)).all? do... in the helper method works.
Can someone please explain how this works? I don't understand how all? is being called on a range.
def ordered_vowel_words(str)
words = str.split(" ")
ordered_vowel_words = words.select do |word|
ordered_vowel_word?(word)
end
ordered_vowel_words.join(" ")
end
def ordered_vowel_word?(word)
vowels = ["a", "e", "i", "o", "u"]
letters_arr = word.split("")
vowels_arr = letters_arr.select { |l| vowels.include?(l) }
(0...(vowels_arr.length - 1)).all? do |i|
vowels_arr[i] <= vowels_arr[i + 1]
end
end
I've added some comments :)
def ordered_vowel_words(str)
# words is a string with words separated by a whitespace.
# split generates an array of words from a string
words = str.split(" ")
# select only the ordered vowel words from the previous array
ordered_vowel_words = words.select do |word|
ordered_vowel_word?(word)
end
# join the ordered vowel words in a single string
ordered_vowel_words.join(" ")
end
def ordered_vowel_word?(word)
# THESE ARE THE VOWELS YOU FOOL
vowels = ["a", "e", "i", "o", "u"]
# transform the word in an array of characters
letters_arr = word.split("")
# select only the vowels in this array
vowels_arr = letters_arr.select { |l| vowels.include?(l) }
# generate a range from 0 to the length of the vowels array minus 2:
# there is this weird range because we want to iterate from the first vowel
# to the second to last; all? when called on a range returns true if...
(0...(vowels_arr.length - 1)).all? do |i|
# for each number in the range, the current vowel is smaller that the next vowel
vowels_arr[i] <= vowels_arr[i + 1]
end
end
Hope this helped!
EDIT I might add that the last block doesn't feel very Ruby-ish. I may suggest this alternative implementation:
def ordered_vowel_word?(word)
vowels = ["a", "e", "i", "o", "u"]
# transform the word in an array of characters
letters_arr = word.split("")
# select only the vowels in this array
vowels_arr = letters_arr.select { |l| vowels.include?(l) }
# from this array generate each possible consecutive couple of characters
vowels_arr.each_cons(2).all? do |first, second|
first <= second
end
end
require 'rspec/autorun'
describe "#ordered_vowel_word?" do
it "tells if word is ordered" do
expect(ordered_vowel_word?("aero")).to be_true
end
it "or not" do
expect(ordered_vowel_word?("rolling")).to be_false
end
end
The all? block is essentially iterating over the vowels_arr array, comparing each value with it's next one. If all the comparisons return true then all? will also return true, which means the array is ordered. If one of the iterations returned false, the return value of all? would also be false, which would mean that the collection is unordered.
You can call all? on a Rangehttp://www.ruby-doc.org/core-1.9.3/Range.html object because Range mixes in the Enumerablehttp://www.ruby-doc.org/core-1.9.3/Enumerable.html module, which is the one that defines all?.
You can verify this by trying the following in irb:
Range.included_modules # => => [Enumerable, Kernel]
The first part (0...(vowels_arr.length - 1)) creates a range from
0 to how many vowels are in the word.
all? iterates over that range and returns true if all for all
elements of the range some condition is true false otherwise.
do |i| introduces a block with i as the variable representing
each element of the range created in step 1.
Finally, the condition is for each index in the range, now represented by i, it checks if vowels_arr[i] <= vowels_arr[i+1] is true.
This is my solution to this problem:
def ordered_vowel_words(str)
vowels_s = str.scan(/[aeiou]/)
vowels_sort = str.scan(/[aeiou]/).sort
vowels_s === vowels_sort ? str : ""
end

Resources