Ruby - Iterating over String that iterates over array - ruby

I want to count vowels in Ruby. The code I have come up with, and it works for one word is:
def count_vowels(string)
vowel = 0
i = 0
while i < string.length
if (string[i]=="a" || string[i]=="e" || string[i]=="i" || string[i]=="o"|| string[i]=="u")
vowel +=1
end
i +=1
end
return vowel
end
My question is this: If I have a list of words, rather than a single one, how do I iterate over the list of words to count the vowels in each word? Would it be something like this?
for each string_in list count_vowels

First of all, to count vowels it's as easy as using the count method:
string.downcase.count('aeiou')
If you have an array of strings, you can use each to iterate over them. You can also use map, which iterates over the collection and maps each result to an array.
['abc', 'def'].map do |string|
{ string => string.downcase.count('aeiou') }
end
This will return an array of hashes, with the keys being the strings and the values being the number of vowels.

This is fairly simple. If you have the list of words as an array, you can just do this:
vowel_count = 0;
words.each { |word| vowel_count += count_vowels word }
Now, vowel_count has the amount of vowels in every word.
You could also do something like this, if you want an array of each count of vowels:
vowel_counts = words.map { |word| count_vowels word }

You can use .count:
string.downcase.count('aeiou')
If you have a list of words, you can do the following:
def count_vowels(string)
string.downcase.count('aeiou')
end
list_of_words.map { |word|
{ word => count_vowels(word) }
}
Example

Related

How to search for the longest word in the string

def my_inject(*args)
return yield false if args.empty? && !block_given?
case args.length
when 1 then args.first.is_a?(Symbol) ? sym = args.first : result = args.first
when 2 then result = args.first
sym = args.last
end
result ||= 0
my_each { |x| result = block_given? ? yield(result, x) : result.send(sym, x) }
result
end
What can I add to this code to make it search for the longest word in Array of strings and were add it?
string.split(" ")
.max_by(&:length)
See Enumerable#max_by.
What can I add to this code to make it search for the longest word in a string
"this is a test of the emergency broadcast system".split(' ').sort {|x,y| y.length <=> x.length}.first
To break this down we:
assign a sentence as a string
split that string into words
sort each word by comparing its length with the previous word's length
take the first result
More information on sorting in Ruby at https://apidock.com/ruby/v2_5_5/Enumerable/sort
Some assumptions:
Each array item only contains one word
You only want the longest word returned, not the position
words = %w[tiny humungous antidisestablishmentarianism medium]
puts words.max_by(&:length)

Method working only with .select or .reject but not working with .map solution

I have to solve the following exercise:
"Implement a first method size_splitter which takes two parameters: an array, and a integer (the size). We will assume that the array only contains words, e.g. Strings, and that the arbitrary rule is to form two groups: the first one with words of the given size (second parameter of the method), and the other group with all the other words.
The size_splitter method should return an array of two arrays - the two groups defined above - with the contents sorted alphabetically."
My solution was that:
def size_splitter(array, size)
words1 = []
words2 = []
filtered_array = [words1, words2]
array.map { |word| words1 << word if word.length == size }
array.map { |word| words2 << word if word.length != size }
return filtered_array.sort
end
The exercise solution is:
def size_splitter(array, size)
first_subarray = array.select { |word| word.length == size }.sort
second_subarray = array.reject { |word| word.length == size }.sort
return [first_subarray, second_subarray]
# OR
# array.sort.partition { |word| word.length == size }
end
But i don't understand why my solution is not working, since it works on pry/irb :( can anyone help?
The major difference between your solution and theirs, is that you are sorting filtered_arrays (which is an array of arrays) but you are required to sort each of the arrays inside filtered_arrays. You can simply change return filtered_array.sort to return filtered_array.map { |array| array.sort } or return filtered_array.map(&:sort).
By the way, it's not a good practice to use map when you don't make use of the return value. You can replace map with each and your code will work the exact same.

calling the method 'count' returns wrong number of arguments

I have a method which takes two parameters. An integer for (max_length) and a string for (text). If the amount of characters per word in the text is >= max_length we remove that word from the array. At the end we count the remaining words in the array.
My method works well until text.count where I encounter 'wrong number of arguments, given 0 expected 1+'
I know this is because we haven't passed any arguments to text.count but I don't want to pass any in as I only want to count the remaining number of words left in the array.
However, if I performed a simple example of
x = ["This", "Will", "Work"]
x.count => 3
Why can't I use this example of count inside my block?
What am i doing wrong?
def timed_reading(max_length, text)
text.split.delete_if do |y|
y.length >= max_length
text.count
end
end
I think this is what you were trying to do
def timed_reading(max_length, text)
text.split.delete_if { |y| y.length >= max_length }.count
end
You could just count words with length less than the max anyway
text.split.count { |y| y.length < max_length }
There's no need to delete words if all you return is the count. You can simply use count with a block:
def timed_reading(max_length, text)
text.split.count{|w| w.length < max_length}
end
You are calling count on the string text and not on the array. You need to rearrange your code so that you are calling count on the result of your delete_if call. Something like this:
def timed_reading(max_length, text)
short_words = text.split.delete_if do |y|
y.length >= max_length
end
short_words.count
end

Appending multiple values to one key in an empty hash

I'm trying to find the string in an array that has the most matches to dictionary words in a file. I store the score (matches) as the key of a hash and the corresponding matching strings as the value to the key. For example:
The string "XXBUTTYATCATYSSX" has three substring word matches. The score for this string would be 3. The string and score are stored in the scores hash as:
scores = { 3 => "XXBUTTYATCATYSSX" }
The string "YOUKKYUHISJFXPOP" also has three matches. This should be stored in the hash as:
scores = { 3 => "XXBUTTYATCATYSSX", "YOUKKYUHISJFXPOP" }
"
scores = { }
#scores = Hash.new { |hash, key| hash[key] = [] }
File.open("#{File.dirname(__FILE__)}/dictionary.txt","r") do |file|
#going to a string in the array
strArray.each_index do |str|
score = 0
match = strArray[str]
#going to a line in the dictionary file
file.each_line do |line|
dictWord = line.strip!.upcase
if match.include? dictWord
score += 1
end
end
#the key in the scores hash equals the score (amount of matches)
#the values in the scores hash are the matched strings that have the score of the key
#scores[score] << match
scores.merge!("#{score}" => match)
end
edit:
I've revised the code above. Now it will not enter into file.each_line do |line| after the first loop
Please help.
With File objects, you can't read them twice. That is, if you read the entire file once with each_line, then you try to do it again, the second time won't do anything because it was already at the end of the file. To read the file again, you need to rewind it with file.rewind before you try to read from it.
The second problem is that you're trying to add to an array that doesn't exist. For example:
scores = {}
scores[3] #=> nil
scores[3] << 'ASDASDASD' # crashes (can't use << with nil)
You need to create an array for each score before you can add words to it. One way to do this would be to check if the key exists before using it, like this:
scores = {}
if scores[3].nil?
scores[3] = []
end
scores[3] << 'word' # this will work
Straight to the code:
scores = Hash.new
File.open("#{File.dirname(__FILE__)}/dictionary.txt","r") do |file|
strings.each do |string|
score = 0
file.each do |line|
score += 1 if string.match(line.strip!.upcase)
end
# store score and new array unless it already have same score
scores.store(score, []) unless scores.has_key?(score)
scores[score] << string
# rewind to read dictionary from first line on next iteration
file.rewind
end
end
strings is your array of strings to compare with dict:
e.g. strings = ["XXBUTTYYOUATCATYSSX", "YOUKKYUHISJFXPOP"])

array modified in the loop causes bug with the output ruby

I have a code that places anagrams into an array of arrays. (which contain anagrams)
but somewhere i made a bug and the first values do not output as arrays but just as strings
I am using the << operator to push one array into the other
the code is not that complicated but i cannot find a bug
def combine_anagrams(words)
indexes = []
anagrams = []
words.each{|word|
if(word.is_a? String )
first_word = word.downcase.chars.sort.join
words.each{|second_word|
if(second_word.is_a? String)
if(first_word == second_word.downcase.chars.sort.join)
indexes << words.index(second_word)
end
end
}
indexes.each{|index| anagrams << words[index] }
words.reject!.with_index {|el, idx| indexes.include?(idx)}
words << anagrams # i replaced words with an array all_anagrams
indexes = []
anagrams = []
end
}
return words
end
puts combine_anagrams([ 'cars','for', 'potatoes', 'racs', 'four','scar', 'creams', 'scream'] ).inspect
outputs
["for", "four", ["cars", "racs", "scar"], ["potatoes"], ["creams", "scream"]]
if i switch the order of "cars" and "for" in the input i get
["cars", "racs", "scar", ["for"], ["potatoes"], ["four"], ["creams", "scream"]]
Whats going on here
Sorry for the messy code im just begging to learn ruby
I created an additional variable all_anagrams = [] to store the array of all anagrams
when i output the array onto the sreen i get all the values except the "for" and "four" for some reason those never get send to all_anagrams
probably because i shorten the array when i am in the loop and those values get skipped over?
However i dont know how to deal with this problem.
the output of all_anagrams is
[["cars", "racs", "scar"], ["potatoes"], ["creams", "scream"]]
What you need is introduce a new array to store anagrams before you blank it, lets call it valid_anagrams. Right now you're pushing that in words. And as Fredrick pointed out you're modifying words while iterating over it. Its not good and to avoid that you keep a clone of words called words_clone and reject items from it instead. Following code should work -
def combine_anagrams(words)
indexes, anagrams, valid_anagrams = [], [], []
words_clone = words.clone # creating a clone of words
words.each do |word|
if(word.is_a? String )
first_word = word.downcase.chars.sort.join
words.each do |second_word|
if(second_word.is_a? String)
if(first_word == second_word.downcase.chars.sort.join)
indexes << words.index(second_word)
end
end
end
indexes.each{|index| anagrams << words[index] }
# reject from words_cloned instead of words
words_clone.reject!.with_index {|el, idx| indexes.include?(idx)}
# insert anagrams into valid_anagrams array. In your code you inserted it in words array
valid_anagrams << anagrams unless valid_anagrams.include?(anagrams)
indexes, anagrams = [], []
end
end
# return valid_anagrams array
return valid_anagrams
end

Resources