Ruby -- Why does += increase the number for my string? - ruby

In the following code the value for "seven" changes from 1 to 2:
word_counts = Hash.new(0)
sample = "If seven maids with seven mops"
sample.split.each do |word|
word_counts[word.downcase] += 1
puts word_counts
end
Output:
{}
{"if"=>1}
{"if"=>1, "seven"=>1}
{"if"=>1, "seven"=>1, "maids"=>1}
{"if"=>1, "seven"=>1, "maids"=>1, "with"=>1}
{"if"=>1, "seven"=>2, "maids"=>1, "with"=>1}
{"if"=>1, "seven"=>2, "maids"=>1, "with"=>1, "mops"=>1}
Can someone explain why it went from 1 to 2?

OK, I'll try..
word_counts[word.downcase] += 1 means word_counts[word.downcase] = word_counts[word.downcase] + 1. Now, on fifth iteration word equals 'seven', so it does word_counts['seven'] = word_counts['seven'] + 1. But word_counts['seven'] was 1, so it becomes 2.
When you split the string you get the array with two strings "seven", because the sentence has two occurrences of that word.
"If seven maids with seven mops".split #=> ["If", "seven", "maids", "with", "seven", "mops"]

Related

Count the number of vowels occurring in all the substrings of given string

I am looking at this challenge:
Given a string of length N of lowercase characters containing 0 or more vowels, the task is to find the count of vowels that occurred in all the substrings of the given string.
Example
Input: str = "abc"
Output: 3
The given string "abc" contains only one vowel = 'a'.
Substrings of "abc" are
{"a", "b", "c", "ab", "bc", "abc"}
Hence, the sum of occurrences of the vowel(s) in these strings is:
3
('a' occurred 3 times).
How to solve the above problem in O(N) time complexity ?
Here are some elements to use in the algorithm:
Let's first count how many substrings can be formed from a string (ignoring vowel counts):
"a" => {"a"} => 1
"ab" => {"ab", "a", "b"} => 1+2 = 3
"abc" => {"abc", "ab", "bc", "a", "b", "c"} => 1+2+3 = 6
"abcd" => {"abcd", "abc", "bcd", "ab", "bc", "cd", "a", "b", "c", "d"} => 1+2+3+4 = 10
...
The pattern is 1+2+3+...+𝑛, where 𝑛 is the length of the string, which is 𝑛(𝑛+1)/2
Now let's take a string that just has one vowel: "klmnopqrst". Then the answer consists of counting the number of substrings which have this vowel.
We know there are 10(10+1)/2 = 55 substrings in total, but many of those counted subtrings do not have a vowel. None of the subtrings of "klmn" have a vowel. There are 4(4+1)/2 = 10 such subtrings. Also none of the subtrings of "pqrst" have a vowel. There are 5(5+1)/2 = 15 such substrings. All other substrings have the vowel. So we can make the subtraction... the output should be 55 - 10 - 15 = 30.
Therefore the general principle is: for each vowel in the input, determine how many substrings do not include that vowel -- by counting the number of substrings at the left, and those at the right of the vowel. This gives us a clue about the number of substrings that do include that vowel -- by subtracting the non-cases from the total number of substrings.
If we do this for each vowel, we will have counted the total occurrences of vowels in all the substrings.
Here is that algorithm expressed in pseudo code:
function occurrence(str):
n := length(str)
total := 0
allcount := n * (n + 1) // 2
for i := 1 to n:
if str[i] is a vowel:
total = total + allcount - (i - 1) * i / 2 - (n - 1 - i) * (n - i) / 2
return total
NB: note that -- as is common in pseudo code -- i is a position (starting at 1), not a zero-based index.
(In case trincot's answer is not enough.)
Each vowel appears in (l + 1) * (r + 1) substrings, where l is the number of characters to the left of the vowel and r the number of characters on the right of the vowel.
Example 1:
"abc"
'a': (0 + 1) * (2 + 1) = 3
Total: 3
Example 2:
"ae"
'a': (0 + 1) * (1 + 1) = 2
'e': (1 + 1) * (0 + 1) = 2
Total: 4

summing up string representation of year and month durations in ruby

I was wondering if there is a way to sum up multiple durations in string representations like 2 years 2 months, 10 months, 3 years and output 6 years
You could do that as follows.
str = "2 years 4 months, 10 months, 3 years, 1 month"
r = /
(\d+) # match one or more digits in capture group 1
\s+ # match one or more whitespace chars
(year|month) # match 'year' or 'month' in capture group 2
s? # optionally match 's'
\b # match a word break
/x # free-spacing regex definition mode
a = str.scan r
#=> [["2", "year"], ["4", "month"], ["10", "month"], ["3", "year"], ["1", "month"]]
h = a.each_with_object(Hash.new(0)) { |(n,period),h| h[period] += n.to_i }
#=> {"year"=>5, "month"=>15}
y, m = h["month"].divmod(12)
#=> [1, 3]
h["year"] += y
#=> 6
h["month"] = m
#=> 3
h #=> {"year"=>6, "month"=>3}
Notes:
As noted in the doc for String#scan, "If the pattern contains groups, each individual result is itself an array containing one entry per group."
Hash.new(0) creates an empty hash with a default value of zero, meaning that if that hash h does not have a key k, h[k] returns zero. Thos is sometimes called a counting hash. See the doc for Hash::new.
Numeric#divmod is a useful and greatly-underused method.

Iterator does not work in a simple loop

Very annoying mistake, but I'm still learning, so please be helpful.
I read a file, make a table and iterate it. Was wondering why my iteration incrementation does not work in this type of loop.
nrOfWordsInOneLine_array = Array.new { Hash.new }
iterator = 0
nrOfWordsInOneLine_array.each_with_index do |i, j|
iterator =+ 1
puts "Word in line #{j+1} #{iterator} is: #{i.length} and the longest one is #{i.max_by(&:length)} with #{i.max_by(&:length).length} letters"
end
output:
Word in line 1 1 is: 8 and the longest one is First with 5 letters
Word in line 2 1 is: 6 and the longest one is Second with 6 letters
Word in line 3 1 is: 4 and the longest one is Fourth with 6 letters
Word in line 4 1 is: 2 and the longest one is Fifth with 5 letters
iterator =+ 1 You have the + and = around the wrong way.
You want iterator += 1, right now you are setting iterator to +1 which is 1.

Ruby: increment all integers in a string by +1

I am looking for a succinct way to increment all the integers found in a string by +1 and return the full string.
For example:
"1 plus 2 and 10 and 100"
needs to become
"2 plus 3 and 11 and 101"
I can find all the integers very easily with
"1 plus 2 and 10 and 100".scan(/\d+/)
but I'm stuck at this point trying to increment and put the parts back together.
Thanks in advance.
You could use the block form of String#gsub:
str = "1 plus 2 and 10 and 100".gsub(/\d+/) do |match|
match.to_i + 1
end
puts str
Output:
2 plus 3 and 11 and 101
The gsub method can take in a block, so you can do this
>> "1 plus 2 and 10 and 100".gsub(/\d+/){|x|x.to_i+1}
=> "2 plus 3 and 11 and 101"
The thing with your regex is that it doesn't preserve your original string in the chain in order to put it back. What I did was to split it using spaces, detect which are words or integers using w.to_i != 0 (not counting 0 as an integer, you might want to improve this), add one, and join it back:
s = "1 plus 2 and 10 and 100"
s.split(" ").map{ |e| if (e.to_i != 0) then e.to_i+1 else e end }.join(" ")
=> "2 plus 3 and 11 and 101"

Ruby Array - highest integer

brand new to Ruby, and love it. Just playing around with the below code:
public
def highest
highest_number = 0
each do |number|
number = number.to_i
highest_number = number if number > highest_number
puts highest_number
end
end
array = %w{1 2 4 5 3 8 22 929 1000 2}
array.highest
So at the moment the response I get is:
1
2
4
5
5
8
22
929
1000
1000
So it puts the array first, then the highest number from the array as well. However all I want it to is put the highest number only...
I have played around with this and can't figure it out! Sorry for such a newbie question
The problem is that you have the puts statement inside the each loop, so during every iteration it prints out what the highest number currently is. Try moving it outside the each loop so that you have this:
public
def highest
highest_number = 0
each do |number|
number = number.to_i
highest_number = number if number > highest_number
end
puts highest_number
end
array = %w{1 2 4 5 3 8 22 929 1000 2}
array.highest
Which produces the desired output:
1000
You could also save yourself some trouble by using max_by:
>> a = %w{1 2 4 5 3 8 22 929 1000 2}
=> ["1", "2", "4", "5", "3", "8", "22", "929", "1000", "2"]
>> m = a.max_by { |e| e.to_i }
=> "1000"
You could also use another version of max_by:
m = a.max_by(&:to_i)
to avoid the extra noise of the "block that just calls a method".
But this is probably a Ruby blocks learning exercise for you so using existing parts of the standard libraries doesn't count. OTOH, it is good to know what's in the standard libraries so punting to max_by or max would also count as a learning exercise.
You can do this instead and avoid the highest_number variable.
array = %w{1 2 4 5 3 8 22 929 1000 2}
class Array
def highest
collect { |x| x.to_i }. \
sort. \
last.to_i
end
end
array.highest # 1000
The collect { |x| x.to_i } can also be written as collect(&:to_i) in this case.

Resources