I need to count the number of vowels,words,pronouns("he,she,them") in each line of a string entered by user. if input is "they are playing. he is studying" the output expected is Sentence 1 has 3 words,has 4 vowels , 1 pronoun. \nSentence 2 has 3 words,4 vowels , 1 pronoun. I have written the following code but getting an error unexpected-end-of-input.
string = gets
string =string.chomp
sentencecount = 0
wordcount = 0
pronouns={"He"=>0,"She"=>0,"They"=>0,"Them"=>0}
procount=0;
string.split(".").each do |sentence|
wordcount = 0
sentencecount += 1 #tracking number of sentences
vowels=sentence.scan(/[aeoui]/).count
procount=0
sentence.split(/\w+/).each do |word|
pronouns.each do|key,value|
if (key.eq word)
procount++
wordcount += 1 # tracking number of words
end
puts "Sentence #{sentencecount} has #{wordcount} words, has #{vowels} vowels"
end
you don't need semicolon at the end of the lines
if requires an end (unless it's the inline form)
++ operator doesn't exist in Ruby
you compare two strings with the == operator (it's a method actually)
string = gets.chomp
sentencecount = 0
wordcount = 0
pronouns = {"He"=>0, "She"=>0, "They"=>0, "Them"=>0}
procount = 0
string.split(".").each do |sentence|
wordcount = 0
sentencecount += 1 #tracking number of sentences
vowels = sentence.scan(/[aeoui]/).count
procount = 0
sentence.split(/\w+/).each do |word|
pronouns.each do|key, value|
if key == word
procount += 1
end
wordcount += 1 # tracking number of words
end
end
puts "Sentence #{sentencecount} has #{wordcount} words, has #{vowels} vowels"
end
Based off of your initial attempt, I've optimized it slightly and make it more readable in terms of code.
string = gets.chomp
pronouns = ['he', 'she', 'they', 'them']
total_word_count = 0
total_procount = 0
total_vowel_count = 0
sentences = string.split(".")
total_sentence_count = sentences.size
sentences.each_with_index do |sentence, idx|
# VOWELS
vowel_count = sentence.scan(/[aeoui]/).count
total_vowel_count += vowel_count
# WORDS
words = sentence.split
sentence_word_count = words.size
total_word_count += sentence_word_count
# PRONOUNS
sentence_procount = 0
words.each do |word|
sentence_procount += 1 if pronouns.include?(word.downcase)
end
total_procount += sentence_procount
puts "Sentence #{idx + 1} has #{sentence_word_count} words, has #{vowel_count} vowels"
end
puts "Input has #{total_sentence_count} sentences, #{total_word_count} words, #{total_vowel_count} vowels, and #{total_procount} pronouns"
I suggest you return an array of hashes, one for each line, each hash containing statistics for the associated line. You can then do what you want with the information in that array.
VOWELS = 'aeiou'
PRONOUNS = %w| he she they them |
#=> ["he", "she", "they", "them"]
PRONOUN_REGEX = /\b#{Regexp.union(PRONOUNS)}\b/
#=> /\b(?-mix:he|she|they|them)\b/
def count_em(str)
str.downcase.split(/\n/).map { |line|
{ vowels: line.count(VOWELS),
words: line.scan(/[[:lower:]]+/).count,
pronouns: line.scan(PRONOUN_REGEX).size } }
end
a = count_em "He thought that she thought that he did not know the truth\n" +
"René knew she would torment them until they went bananas\n"
#=> [{:vowels=>14, :words=>12, :pronouns=>3},
# {:vowels=>15, :words=>10, :pronouns=>3}]
a.each.with_index(1) { |h,i|
puts "In line #{i} there are %d vowels, %d words and %d pronouns" %
h.values_at(:vowels, :words, :pronouns) }
# In line 1 there are 14 vowels, 12 words and 3 pronouns
# In line 2 there are 15 vowels, 10 words and 3 pronouns
puts "The total number of vowels in all lines is %d" %
a.map { |h| h[:vowels] }.reduce(:+)
# The total number of vowels in all lines is 29
This solution is incomplete in that it doesn't deal with contractions ("don't"), possessives ("Mary's"), abbreviations ("1st"), initials and numeric parts of names ("J. Philip ('Phil') Sousa III") and other twists of the English language.
Related
I had a technical test for an entry-level job 2 days ago. It went well apart from the last assessment.
I will go over the assessment with the CTO tomorrow and was hoping I could get help to get my head around this one, so I do not sound clueless.
It was something like this:
Given string as an argument, give us palindrome method that would check if a palindrome of a minimum 3 characters can be created by removing 1 or 2 characters. The string has no special characters, numbers or whitespace basically only letters (eg: str = "abecbea")
If true, print letters removed, if false print "not possible"
"remove 1 or 2 characters" and "print letters removed" is giving me a headache legit
I have tried a lot of different things but for the last 2 days but i am completely stuck!
[EDIT]
Ideas i started with below
def is_palindrome(s)
s == s.reverse && s.length >= 3
end
def possible_palin_by_removing_one_char(s)
array_chars = s.chars
first_char = array_chars[0]
last_char = array_chars[-1]
while first_char < last_char
if first_char != last_char
first_char += 1
last_char -= 1
else
if is_palindrome
puts ????
else
puts "not possible"
end
end
end
end
or
def palindrome?(string)
deleted_chars = []
candidates = 0.upto(string.length-1).map do |index|
array_chars = string.chars
deleted_chars << array_chars.delete_at(index)
array_chars
end
if candidates.any? { |c| c.reverse == c } && string.length >= 3
puts "It is a palindrome with letters '#{deleted_chars.join(",")}' removed !"
# puts deleted_chars
else
if string.length <= 3
puts "String '#{string}' is too short"
else
puts "This is not possible to create palindrome with string '#{string}'"
end
end
end
palindrome?("abcecbae")
I would love someone to help me solve this one
Thanks heaps for your help
Put all chars in an Array (ar = str.chars)
Try all combinations which are 2 less than
the size of the array (so 5 in the example "abcecbae")(ar.combination(5))
Select all combinations which happen to be equal to their reverse
Map the result(s) back from arrays to strings
Similar for 1 removed char.
This task might be a little bit trickier without Ruby's rich standard library support.
Here is how I'd solve this task in the 1st iteration:
Check if the original string is a palindrome, return immediately if it is
Build a list of possible combinations of indices to remove
Iterate over this list, check if removing the chars at given indices makes our input string a palindrome
The trickiest part here is (2), but Enumerable#permutation almost does the job.
So, something like
def palindrome?(s)
s == s.reverse
end
def indices_to_remove(string)
ary = (0..string.size-1).to_a
# Permutations like [0,1] and [1,0] means the same in our context, so we need to remove this duplication.
(ary.map { |x| [x] } + ary.permutation(2).map(&:sort)).uniq
end
def remove_chars(str, *indices)
str.dup.tap do |s|
indices.sort.reverse_each { |i| s[i] = '' }
end
end
def find_palindrome(str)
return if palindrome?(str)
indices_to_remove(str).each do |indices|
new_str = remove_chars(str, *indices)
return "Letters removed: #{indices.inspect}; palindrome: '#{new_str}'" if palindrome?(new_str)
end
end
That's it. Let's see if i works:
pry(main)> find_palindrome("abecbea")
=> "Letters removed: [1, 3]; palindrome: 'aebea'"
It does. Adjust the filtering logic (check palindroms' size as needed) and output if necessary.
Thanks all for your help. With the help of a senior dev, we came up with the below solution
def palindrome_check(str)
length = str.length
# convert to array of chars
chars = str.chars
# check if palindrome by deleting 1 characters
length.times do |i|
char = chars.delete_at(i)
return "Letter '#{char}' has been removed from String '#{str}' to create palindrome" if chars == chars.reverse
# return the array to original condition
chars.insert(i, char)
end
# only do remove 2 characters check if length > 4, as otherwise removing would result in a string less than 3 characters
if length > 4
length.times do |i|
length.times do |j|
# avoid repeating the same checks
next if j <= i
# since j is always greater than i, remove j first to avoid affecting which character is at position i
char_two = chars.delete_at(j)
char_one = chars.delete_at(i)
return "Letters '#{[char_one, char_two].join(' and ')}' has been removed from String '#{str}' to create palindrome" if chars == chars.reverse
# return the array to original condition
chars.insert(i, char_one)
chars.insert(j, char_two)
end
end
end
return "'#{str}' can't be a Palindrome"
end
palindrome_check('string') # 'can't be a Palindrome'
palindrome_check('abcdcfba') # 'f'
palindrome_check('rqubyxyburx') # 'q, x'
Write a method that returns the no of various lowercase, uppercase, digits and special characters used in the string. Make use of Ranges.
Input = "heLLo Every1"
I am making using of ranges and case method in solution provided.
Solution:
class String
def character_count
uppercase_count = 0
lowercase_count = 0
digit_count = 0
uppercase_range = Range.new('A', 'Z')
lowercase_range = Range.new('a', 'z')
digit_range = Range.new('0', '9')
special_character_count = 0
each_char do |item|
case item
when uppercase_range
uppercase_count += 1
when lowercase_range
lowercase_count += 1
when digit_range
digit_count += 1
else
special_character_count += 1
end
end
[lowercase_count, uppercase_count, digit_count, special_character_count]
end
end
if ARGV.empty?
puts 'Please provide an input'
else
string = ARGV[0]
count_array = string.character_count
puts "Lowercase characters = #{count_array[0]}"
puts "Uppercase characters = #{count_array[1]}"
puts "Numeric characters = #{count_array[2]}"
puts "Special characters = #{count_array[3]}"
end
Code is working.
Yes
class String
def character_count
counters = Hash.new(0)
each_char do |item|
case item
when 'A'..'Z'
counters[:uppercase] += 1
when 'a'..'z'
counters[:lowercase] += 1
when '0'..'9'
counters[:digit] += 1
else
counters[:special] += 1
end
end
counters.values_at(:uppercase, :lowercase, :digit, :special)
end
end
if ARGV.empty?
puts 'Please provide an input'
else
string = ARGV[0]
uppercase, lowercase, digit, special = string.character_count
puts "Lowercase characters = #{lowercase}"
puts "Uppercase characters = #{uppercase}"
puts "Numeric characters = #{digit}"
puts "Special characters = #{special}"
end
You can instead use regex in better way as following,
type = { special: /[^0-9A-Za-z]/, numeric: /[0-9]/, uppercase: /[A-Z]/, lowercase: /[a-z]/ }
'Hello World'.scan(type[:special]).count
# => 1
'Hello World'.scan(type[:numeric]).count
# => 0
'Hello World'.scan(type[:uppercase]).count
# => 2
'Hello World'.scan(type[:lowercase]).count
# => 8
Other option.
First, map your ranges into an Hash:
mapping = { upper: ('A'..'Z'), lower: ('a'..'z'), digits: ('0'..'9'), specials: nil }
Then initialize the recipient Hash to default 0:
res = Hash.new(0)
Finally, map the chars of the input:
input = "heLLo Every1"
input.chars.each { |e| res[(mapping.find { |k, v| v.to_a.include? e } || [:specials]).first ] += 1 }
res
#=> {:upper=>3, :lower=>7, :digits=>1, :specials=>1}
str = "Agent 007 was on the trail of a member of SPECTRE"
str.each_char.with_object(Hash.new(0)) do |c,h|
h[ case c
when /\d/ then :digit
when /\p{Lu}/ then :uppercase
when /\p{Ll}/ then :downcase
else :special
end
] += 1
end
end
#=> {:uppercase=>8, :downcase=>28, :special=>10, :digit=>3}
I'm writing a program which takes input, stores it as a hash and sorts the values.
I'm having trouble comparing a current hash value with a variable.
Sample Input:
3
A 1
B 3
C 5
A 2
B 7
C 2
Sample Output:
A 1 2
B 3 7
C 2 5
Everything works apart from this part, and I'm unsure why.
if values.key?(:keys)
if values[keys] >= val
values.store(keys,val.prepend(val + " "))
else
values.store(keys,val.concat(" " + val))
end
else
values.store(keys,val)
end
i = i + 1
end
Rest of code:
#get amount of records
size = gets.chomp
puts size
size = size.to_i
values = Hash.new(0)
i = 0
while i < (size * 2)
text = gets.chomp
#split string and remove space
keys = text.split[0]
val = text.split[1]
#check if key already exists,
# if current value is greater than new value append new value to end
# else put at beginning of current value
if values.key?(:keys)
if values[keys] >= val
values.store(keys,val.prepend(val + " "))
else
values.store(keys,val.concat(" " + val))
end
else
values.store(keys,val)
end
i = i + 1
end
#sort hash by key
values = values.sort_by { |key, value| key}
#output hash values
values.each{|key, value|
puts "#{key}:#{value}"
}
Could anyone help me out? It would be most appreciated.
The short answer is that there are two mistakes in your code. Here is the fixed version:
if values.key?(keys)
if values[keys] >= val
values.store(keys,values[keys].prepend(val + " "))
else
values.store(keys,values[keys].concat(" " + val))
end
else
values.store(keys,val)
end
The if statement was always evaluating as false, because you were looking for hash key named :keys (which is a Symbol), not the variable you've declared named keys.
Even with that fixed, there was a second hidden bug: You were storing a incorrect new hash value. val.concat(" " + val) would give you results like A 2 2, not A 1 2, since it's using the new value twice, not the original value.
With that said, you code is still very confusing to read... Your variables are size, i, text, val, values, key and keys. It would have been a lot easier to understand with clearer variable names, if nothing else :)
Here is a slightly improved version, without changing the overall structure of your code:
puts "How may variables to loop through?"
result_length = gets.chomp.to_i
result = {}
puts "Enter #{result_length * 2} key-value pairs:"
(result_length * 2).times do
input = gets.chomp
input_key = input.split[0]
input_value = input.split[1]
#check if key already exists,
# if current value is greater than new value append new value to end
# else put at beginning of current value
if result.key?(input_key)
if result[input_key] >= input_value
result[input_key] = "#{input_value} #{result[input_key]}"
else
result[input_key] = "#{result[input_key]} #{input_value}"
end
else
result[input_key] = input_value
end
end
#sort hash by key
result.sort.to_h
#output hash result
result.each{|key, value|
puts "#{key}:#{value}"
}
h = Hash.new { |h,k| h[k] = [] }
input = ['A 1', 'B 3', 'C 5', 'A 2', 'B 7', 'C 2'].join("\n")
input.each_line { |x| h[$1] << $2 if x =~ /^(.*?)\s+(.*?)$/ }
h.keys.sort.each do |k|
puts ([k] + h[k].sort).join(' ')
end
# A 1 2
# B 3 7
# C 2 5
This would be a more Ruby-ish way to write your code :
input = "A 1
B 3
C 5
A 2
B 7
C 2"
input.scan(/[A-Z]+ \d+/)
.map{ |str| str.split(' ') }
.group_by{ |letter, _| letter }
.each do |letter, pairs|
print letter
print ' '
puts pairs.map{ |_, number| number }.sort.join(' ')
end
#=>
# A 1 2
# B 3 7
# C 2 5
Here is my code in ruby for a word compression.
For any given word (e.g. abbbcca) the compressed word/output should be in the format as "letter+repetition" (for above example, output: a1b3c2a1).
Here I'm so close to the completion but my result isn't in the expected format. It's counting the whole letters in string.chars.each thus resulting output as a2b3c2a2.
Any help?
def string_compressor(string)
new_string = []
puts string.squeeze
string.squeeze.chars.each { |s|
count = 0
string.chars.each { |w|
if [s] == [w]
count += 1
end
}
new_string << "#{s}#{count}"
puts "#{new_string}"
}
if new_string.length > string.length
return string
elsif new_string.length < string.length
return new_string
else "Equal"
end
end
string_compressor("abbbcca")
'abbbcca'.chars.chunk{|c| c}.map{|c, a| [c, a.size]}.flatten.join
Adapted from a similar question.
Similar:
'abbbcca'.chars.chunk{|c| c}.map{|c, a| "#{c}#{a.size}"}.join
See chunk documentation
You can use a regular expression for that.
'abbbcca'.gsub(/(.)\1*/) { |m| "%s%d" % [m[0], m.size] }
#=> "a1b3c2a1"
The regular expression reads, "match any character, capturing it in group 1. Then match the contents of capture group 1 zero or more times".
As you said, your code counts every letter in the string, not just the one grouped next to one another.
Here's a modified version :
def display_count(count)
if count == 1
""
else
count.to_s
end
end
def string_compressor(string)
new_string = ''
last_char = nil
count = 0
string.chars.each do |char|
if char == last_char
count += 1
else
new_string << "#{last_char}#{display_count(count)}" if last_char
last_char = char
count = 1
end
end
new_string << "#{last_char}#{display_count(count)}" if last_char
new_string
end
p string_compressor('abbbcca') #=> "ab3c2a"
p string_compressor('aaaabbb') #=> "a4b3"
p string_compressor('aabb') #=> "a2b2"
p string_compressor('abc') #=> "abc"
Note that with display_count removing 1s from the string, new_string can never be longer than string. It also probably isn't a good idea to return Equal as a supposedly compressed string.
To decompress the string :
def string_decompressor(string)
string.gsub(/([a-z])(\d+)/i){$1*$2.to_i}
end
p string_decompressor("a5b11") #=> "aaaaabbbbbbbbbbb"
p string_decompressor("ab3c2a") #=> "abbbcca"
str = 'put returns between paragraph put returns between paragraph put returns between paragraph'
def word_count(string)
resut= []
return result = string.split.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
end
def parse_word(word)
word.gsub!(/[^a-zA-Z0-9]/, " ")
word.downcase!
#yoo= word
end
result =word_count(str)
print result, "\n\n"
res2 = result.select { |pair| pair[1] > 1 } `#Error coming`
I am getting OutPut
**
OutPut
**
{"put"=>3, "returns"=>3, "between"=>3, "paragraph"=>3}
I need OutPut Like this
**
OutPut
**
{"put"=>3, "returns"=>3, "between"=>3, "paragraph"=>3}
and
put: 3
returns: 3
between: 3
but the main problem is that he gave us the code to do that but i cant able to understand it
I am not getting this what this code will do can anyone help me ...And modify it so it can work
The following processes the first paragraph of put returns ... Note that ss is an array of those words that occur at least twice in this paragraph.
nect = ss.select { |p| p[1] > 1 }
nect .sort.each do |key, count|
puts "#{key}: #{count}"
end
module WordCount
def self.word_count(s)
count_frequency(words_from_string(s))
end
def self.word_count_from_file(filename)
s = File.open(filename) { |file| file.read }
word_count(s)
end
def self.words_from_string(s)
s.downcase.scan(/[\w']+/)
end
def self.count_frequency(words)
counts = Hash.new(0)
for word in words
counts[word] += 1
end
# counts.to_a.sort {|a,b| b[1] <=> a[1]}
# sort by decreasing count, then lexicographically
counts.to_a.sort do |a,b|
[b[1],a[0]] <=> [a[1],b[0]]
end
end
end
def word_count(s)
WordCount.word_count(s)
end