def most_common_letter(string)
letter = nil
final_letter = nil
letter_count = nil
idx1 = 0
idx2 = 0
count = 0
while idx1 < string.length
letter = string[idx1]
while idx2 < string.length
if letter == string[idx2]
count += 1
end
idx2 += 1
end
if (letter_count == nil) || (count > letter_count)
letter_count = count
final_letter = letter
end
idx1 += 1
end
arr = []
arr.push(final_letter)
arr.push(letter_count)
return arr
end
puts(most_common_letter("abbab") == ["b", 3])
#should be true
Keep getting [a,2] which leads me to believe it is only executing parts of the code when in my mind the code should be resetting the value of final letter and letter count every time there is a more frequent letter. Where have I gone wrong?
Often a counting hash is used for this type of problem:
def most_frequent_letter(str)
str.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }.max_by(&:last)
end
most_frequent_letter("bananas are so-so")
#=> ["a",4]
The steps:
h = "bananas are so-so".each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }
#=> {"b"=>1, "a"=>4, "n"=>2, "s"=>3, " "=>2, "r"=>1, "e"=>1, "o"=>2, "-"=>1}
h.max_by(&:last)
#=> ["a",4]
Here is an easier way to solve this:
def most_common_letter(string)
letters = string.split('').sort!
frequency = letters.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
frequency.max_by{|k,v| v}
end
most_common_letter("abbab")
=> ["b", 3]
Let me go through this code line-by-line to explain.
1) Get the letters of the string and sort them alphabetically:
string = "abbab"
letters = string.split('').sort!
=> ["a", "a", "b", "b", "b"]
2) Create a hash with keys (letters) and values (occurrence)
frequency = letters.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
=> {"a"=>2, "b"=>3}
3) Find the highest value, and return the key value pair as an array.
frequency.max_by{|k,v| v}
=> ["b", 3]
The problem you are running into is that you are not resetting your idx2 after the loop.
You can debug this if you puts the idx two as follows:
def most_common_letter(string)
letter = nil
final_letter = nil
letter_count = nil
idx1 = 0
idx2 = 0
count = 0
while idx1 < string.length
letter = string[idx1]
while idx2 < string.length
if letter == string[idx2]
count += 1
end
idx2 += 1
end
puts "#{idx2}" #=> This is always 5
if (letter_count == nil) || (count > letter_count)
letter_count = count
final_letter = letter
end
idx1 += 1
end
arr = []
arr.push(final_letter)
arr.push(letter_count)
return arr
end
puts(most_common_letter("abbab"))
Your algorithm is simply doing the following:
comparing idx1 with idx2:
str[0] with str[0]
str[0] with str[1]
str[0] with str[2]
str[0] with str[3]
str[0] with str[4]
then idx2 is out of range and will never satisfy idx2 < string.length again, because the value is not reset.
This is where the logical error is. I won't give you another solution because it seems like you are trying to figure this out on your own. Hope this helps.
Try this
def most_common_letter(str)
str.chars.
group_by{|v| v}.
map{|k, l| [k, l.size]}.
sort_by{|k, size| size}.
last
end
Now:
puts(most_common_letter("abbab") == ["b", 3])
# true
Related
Is there a way if a string is close to a string in a table it will replace it with the one in the table?
Like a spellcheck function, that searches through a table and if the input is close to one in the table it will fix it , so the one in the table and the string is the same?
You can use this code :) Reference code is from here : https://github.com/badarsh2/Algorithm-Implementations/blob/master/Levenshtein_distance/Lua/Yonaba/levenshtein.lua
local function min(a, b, c)
return math.min(math.min(a, b), c)
end
local function matrix(row,col)
local m = {}
for i = 1,row do m[i] = {}
for j = 1,col do m[i][j] = 0 end
end
return m
end
local function lev(strA,strB)
local M = matrix(#strA+1,#strB+1)
local i, j, cost
local row, col = #M, #M[1]
for i = 1, row do M[i][1] = i - 1 end
for j = 1, col do M[1][j] = j - 1 end
for i = 2, row do
for j = 2, col do
if (strA:sub(i - 1, i - 1) == strB:sub(j - 1, j - 1)) then cost = 0
else cost = 1
end
M[i][j] = min(M[i-1][j] + 1,M[i][j - 1] + 1,M[i - 1][j - 1] + cost)
end
end
return M[row][col]
end
local refTable = {"hell", "screen"}
local function getClosestWord(pInput, pTable, threesold)
cDist = -1
cWord = ""
for key, val in pairs(pTable) do
local levRes = lev(pInput, val)
if levRes < cDist or cDist == -1 then
cDist = levRes
cWord = val
end
end
print(cDist)
if cDist <= threesold then
return cWord
else
return pInput
end
end
a = getClosestWord("hello", refTable, 3)
b = getClosestWord("screw", refTable, 3)
print(a, b)
Third parameter is threesold, if min distance is higher than threesold, word is not replaced.
I made a Tic-Tac-Toe game in Ruby. The method below checks for a winner in the vertical columns.
How do I make it so that this method can be applied to boards of different sizes, like 4x4, 6x6?
def vertical_check(array)
result = nil
if (array[0][0] == "X" && array[1][0] == "X" && array[2][0] == "X") ||
(array[0][1] == "X" && array[1][1] == "X" && array[2][1] == "X") ||
(array[0][2] == "X" && array[1][2] == "X" && array[2][2] == "X")
result = "X"
elsif (array[0][0] == "O" && array[1][0] == "O" && array[2][0] == "O") ||
(array[0][1] == "O" && array[1][1] == "O" && array[2][1] == "O") ||
(array[0][2] == "O" && array[1][2] == "O" && array[2][2] == "O")
result = "O"
else
result = nil
end
return result
end
The following is a failed attempt:
def vertical_check_x(array)
result = nil
index = 0
index2 = 0
until result != nil || index == array.length
while array[index][index2] == "X"
index += 1
end
if index == array.length
result = "X"
else
result = nil
index = array.length
end
index2 += 1
end
return result
end
def vertical_check_o(array)
result = nil
index = 0
index2 = 0
until result != nil || index == array.length
while array[index][index2] == "O"
index += 1
end
if index -1 == array.length
result = "O"
else
result = nil
index = array.length
end
index2 += 1
end
return result
end
def vertical_check(array)
result = vertical_check_x(array)
if result == nil
result = vertical_check_o(array)
end
return result
end
To quickly find a winner in given array, count the number of unique elements, confirm that there is only one unique element and if it is only X or O:
def winner arr
return arr[0] if arr.uniq.length == 1 && ['X', 'O'].include?(arr[0])
nil
end
The next problem is selecting the rows, columns and diagonals for an nxn array.
Rows are easy:
rows = arr.map {|row| row}
Columns are as follows - you select elements with the same index for each row:
cols = n.times.collect {|i| arr.map {|row| row[i]}}
Next are diagonals. There are two diagonals, one starts from leftmost corner and the other from the rightmost.
The leftmost diagonal has the sequence as:
(0, 0) -> (1, 1) -> (2, 2) ....
See the pattern?
diag = n.times.collect {|i| arr[i][i]}
The rightmost diagonal has pattern that goes like this (for a 3x3):
(0, 2) -> (1, 1) -> (2, 0)
For a 4x4, it's like this:
(0, 3) -> (1, 2) -> (2, 1) -> (3, 0)
So, the pattern for an nxn is:
(0, n-1-0) -> (1, n-1-1) -> (2, n-1-2) -> ... (i, n-1-i) ... -> (n-1, 0)
So:
diag = n.times.collect {|i| arr[i][n - 1 - i]}
Now, you can just do something like:
w = rows.map {|r| winner r}.compact[0]
for each array to get the winner.
For some reason, string is getting rewritten as mirror by the operation mirror[j] = string[i]
I have no idea why this would be, I'm not commanding it to. The outputs of my reverse(string) function will stop at the middle, like:
ABCDEF -> FEDDEF
but I'm trying to do:
ABCDEF -> FEDCBA
def reverse(string)
mirror = string
i = string.length - 1
j = 0
while j < string.length
mirror[j] = string[i]
puts(mirror)
j = j + 1
i = i - 1
end
return mirror
end
You need to use #dup. In your case, mirror = string means both the variables are holding the reference of the same object. So, when you change the object, that can be reflected through both string and mirror.
def reverse(string)
mirror = string.dup
i = string.length - 1
j = 0
while j < string.length
mirror[j] = string[i]
j += 1 # same as j = j + 1
i -= 1 # same as i = i - 1
end
mirror
# last expression is returned by default, so you can write only `mirror` instead of `return mirror`
end
p reverse 'ABCDEF' # "FEDCBA"
Write a method that takes in a string of lowercase letters (no uppercase letters, no repeats). Consider the substrings of the string: consecutive sequences of letters contained inside the string.
Find the longest such string of letters that is a palindrome.
Based on local method Palindrome?(string), I implemented longest-palindrome(string) as below with test cases:
def palindrome?(string)
i = 0
while i < string.length
if string[i] != string[(string.length - 1) - i]
return false
end
i += 1
end
return true
end
def longest_palindrome(string)
dix = 0
lstr = ""
lstrc = nil
while dix < string.length
dix2 = 1
while dix2 < string.length
str = string.slice(dix,dix2)
count = str.length
if palindrome?(str)
if lstrc == nil || lstrc < count
lstr = str
lstrc = count
end
end
dix2 += 1
end
dix += 1
end
puts(lstr)
return lstr
end
# These are tests to check that your code is working. After writing
# your solution, they should all print true.
puts(
'longest_palindrome("abcbd") == "bcb": ' +
(longest_palindrome('abcbd') == 'bcb').to_s
)
puts(
'longest_palindrome("abba") == "abba": ' +
(longest_palindrome('abba') == 'abba').to_s
)
puts(
'longest_palindrome("abcbdeffe") == "effe": ' +
(longest_palindrome('abcbdeffe') == 'effe').to_s
)
Test results as below:
bcb
longest_palindrome("abcbd") == "bcb": true
bb
longest_palindrome("abba") == "abba": false
effe
longest_palindrome("abcbdeffe") == "effe": true
Why did the second test failed?
... this line is preventing you from considering the entire string
while dix2 < string.length
So when dix is the whole string, you're not doing any testing for palindromes
Change the line to...
while dix2 <= string.length
It would actually be slightly more efficient if you did...
while dix2 <= string.length - dix
Which would prevent you from testing (for, say, a string of length 10), string(7,3) and string(7,4) and string(7,5) etc. etc., which are all basically the same string.
def longest_word(string)
words = string.split
idx = 0
while idx < words.length
if words[idx].length > words[idx + 1].length
longest = words[idx]
else
longest = words [idx + 1]
end
idx += 1
end
return longest
end
puts(longest_word("peas rambling tattoo") == "rambling")
keep getting error message
longest_word.rb:5:in longest_word': undefined methodlength' for nil:NilClass (NoMethodError)
from longest_word.rb:15:in `'
any information on why this is happening would be great
words.length returns the number of elements, but the index starts with 0.
You need a (words.length - 1)
def longest_word(string)
words = string.split
idx = 0
while idx < (words.length - 1)
if words[idx].length > words[idx + 1].length
longest = words[idx]
else
longest = words [idx + 1]
end
idx += 1
end
return longest
end
puts(longest_word("peas rambling tattoo") == "rambling")
Your code is not 'rubyesk', I would prefer:
def longest_word(string)
longest = ''
string.split.each do |word|
longest = word.length > longest.length ? word : longest
end
return longest
end
puts(longest_word("peas rambling tattoo") == "rambling")
or maybe better:
def longest_word(string)
longest = ''
string.split.each do |word|
longest = word if word.length > longest.length
end
return longest
end
puts(longest_word("peas rambling tattoo") == "rambling")
You can also use max_by to get shorter code:
def longest_word(string)
string.split.max_by{|word| word.length}
end
puts(longest_word("peas rambling tattoo") == "rambling")
Or even shorter with string.split.max_by(&:length)