String comparison in Ruby - ruby

I have try to match some conditions for
each letter instance in a string may only participate in one match.
if a letter instance can participate in either n1 or n2, it is to be counted towards n1.
the two strings may or may not be of the same length.
I have try to match on the basis of these conditions bit still getting issue while testing for long string or odd string comparisons.
def matchval_not_pos(str1, str2)
a1, a2 = str1.chars, str2.chars
[a1.size, a2.size].min.times do |i|
if a1[i] == a2[i]
a1.delete_at(i)
a2.delete_at(i)
end
end
a1.reduce(0) do |t,c|
i = a2.index(c)
if i
inc = 1
a2.delete_at(i)
else
inc = 0
end
t + inc
end
end
Some examples (str1, str2 -> n1,n2):
"abc", "abc" -> 3,0
"bca", "abc" -> 0,3
"aab", "abb" -> 2,0
"aab", "aba" -> 1,2
"abc", "aaa" -> 1,0

As I understand, you have two strings, s1 and s2, from which you obtain two strings:
ss1 = s1[0,n]
ss2 = s2[0,n]
where
n = [s1.size, s2.size].min
and you want to know if the characters of ss2 can be rearranged to equal ss1. If my understanding is correct, that would be true if and only if the following is true:
ss1.chars.sort == ss2.chars.sort
Example:
s1 = "ebcda"
s2 = "adcbefr"
n = [s1.size, s2.size].min #=> 5
ss1 = s1[0,n] #=> "ebcda"
ss2 = s2[0,n] #=> "adcbe"
ss1.chars.sort == ss2.chars.sort #=> true

Related

How to find an array of all the words in json that can be made by rearranging these letters(Ruby)

I am new to ruby. How do I write a function that takes a find method that accepts a lower case and the find should return an array of all the words in dic.json that can be made by rearranging these letters. So if I input "ab" then the output should be ["ab", "ba"] from the JSON file below.
dic.json
[
"ab",
"ba",
"abc",
"acb",
"bac",
"bca",
"cab",
"cba"
]
This is what have so far
I used File read to access the JSON file and I have a function that can find permutations but I am not sure how to connect the two functions.
class LetterLocater
def get_file_contents
return File.read('dictionary.json').split
end
def permutation(letters)
return [''] if letters.empty?
chrs = letters.chars
(0...letters.size).flat_map { |i|
chr, rest = letters[i], letters[0...i] + letters[i+1..-1]
permutation(rest).map { |sub|
chr + sub
}
}
end
end
a = LetterLocater.new
puts a.permutation(gets.chomp) ```
Instead of creating all permutations for various inputs, you could also group the words from the dictionary by sorting their letters:
def sorted(str)
str.chars.sort.join
end
# assume this was read from the JSON file
dict = %w[ab ba abc acb bac bca cab cba]
lookup_hash = dict.group_by { |word| sorted(word) }
#=> {
# "ab" => ["ab", "ba"],
# "abc" => ["abc", "acb", "bac", "bca", "cab", "cba"]
# }
Although this calculation can be quite expensive for larger dictionaries, you only have to do it once. (you could even store the hash to disk and only update when your dictionary changes)
After creating the hash, it's almost trivial to find the permutations. You just have to fetch the values for the sorted input:
input = gets.chomp
puts lookup_hash[sorted(input)]
This will be much faster than generating all permutation each time.
In Ruby there is already Array#permutation that you can use to calculate all possible words.
letters = "ab" # example
permutations = letters.split(//).permutation.map(&:join)
#=> ["ab", "ba"]
And then there is 'Array#&' that returns only elements from an array that are present in another array.
words = ["ab", "ba", "abc", "acb", "bac", "bca", "cab", "cba"]
words & permutations
#=> ["ab", "ba"]
And you can use JSON.load(File.open('dictionary.json')) to load the JSON file into a Ruby array – as Schwern already wrote in his comment.
Now let's combine all these methods into one class
require 'json'
class LetterLocater
attr_reader :words
def initialize(dictionary)
#words = JSON.load(File.open('dictionary.json'))
end
def permutation(letters)
permutations = letters.split(//).permutation.map(&:join)
words & permutations
end
end
ll = LetterLocater.new('dictionary.json')
ll.permutation('ab')
#=> ["ab", "ba"]
ll.permutation('abc')
#=> ["abc", "acb", "bac", "bca", "cab", "cba"]
def find_permutations_in_array(arr, str)
chars = str.chars.sort
arr.inject([]) do |res, word|
res << word if word.size == str.size && word.chars.sort == chars
res
end
end
Assumption
I have assumed that the task is one-off, in the sense that the task is to be performed for a single word and a given dictionary, not for possibly many words and the same dictionary.
Approach
I propose that three tests be used to determine if each dictionary word is formed by permuting the letters in the given word:
reject the dictionary word if its length differs from that of the given word
if the above test fails, reject the dictionary word if it begins with a string s where it has been determined that no word beginning with s can be formed by permuting the letters of the given word
if the above test fails, each character in the dictionary is examined in sequence until a character is found that is not present in the given word or whose count in the dictionary word exceeds the count of the same character in the given word, in which case the dictionary word can be rejected
If none of the above tests succeeds we may conclude that the dictionary word can be formed by permuting the letters of the given word.
Note that, in the third test above, if the dictionary word w was rejected when its character at index i was examined we may conclude that no dictionary word that begins with s = w[0,i] can be formed by permuting the characters of the given word, which is the basis for the second test.
Code
def find_em(word, dict)
word_len = word.length
ltr_cnt = word.each_char.tally
prefix = ''
dict.select do |w|
next false if w.length != word_len ||
(prefix.length > 0 && w.start_with?(prefix))
prefix = ''
lc = ltr_cnt.dup
ltrs_used = 0
w.each_char do |c|
prefix << c
break unless lc.key?(c) && lc[c] > 0
lc[c] -= 1
ltrs_used += 1
end
if ltrs_used == word_len
prefix = ''
true
else
false
end
end
end
Example
word = "aab"
dict = [
"aaa", "aab", "ab", "aba", "abb", "aca", "acad", "acb", "acc",
"acd", "ace", "ba", "bac", "bca", "bcc", "caa", "cab", "cbb"
]
Being a "dictionary", I assume the words of dict are ordered lexicographically, but if that is not the case the first step is to sort the given array words to produce dict.
find_em(word, dict)
#=> ["aab", "aba"]
Explanation
For the example just given the method processes each word in the dictionary in as indicated by the following:
"aaa", "aab", "ab", "aba", "abb", "aca", "acad", "acb", "acc",
c m s m c n s r r
"acd", "ace", "ba", "bac", "bca", "bcc", "caa", "cab", "cbb"
r r s n n r n r r
"aaa" is rejected (signified by "c", for "count"), when the third character is examined, because word contains only two "a"'s (which is why "c" is shown under the third "a")
"aab" is matched (signified by "m") because its letters are a permutation of the letters of word
"ab" is rejected because the length of the word is not the same as the length of word
"aba" is matched
"abb" is rejected when the second "b" is examined because there are too many "b"'s
"aca" is rejected when "c" is examined because word does not contain "c" (indicated by the "n" under the "c")
"acad" is rejected because it is the wrong size
"acb", "acc", "acd", "ace" are all rejected because we know from the processing of "aca" that no words starting with "ac" can match (indicated by "r" for "repeat")
"ba" is the wrong size
"bac" is rejected because word does not contain "c"
"bca" does not begin "bac" so we examine each letter from the start of the word until we find "c", which is not contained in word so we reject the word
"bcc" is rejected because we know from examining "bca" that no words starting with "bc" can match
"caa" is rejected because word does not contain "c"
"cab" and "cbb" are rejected because we know from examining "caa" that no words starting with "c" can match
To better understand details of the calculations one might execute the method after having salted it with puts statements. For example, one might execute the following modification.
def find_em(word, dict)
word_len = word.length
ltr_cnt = word.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }
prefix = ''
dict.select do |w|
puts "w=#{w}, prefix=#{prefix}"
next false if w.length != word_len ||
(prefix.length > 0 && w.start_with?(prefix))
prefix = ''
lc = ltr_cnt.dup
ltrs_used = 0
w.each_char do |c|
puts " prefix=#{prefix}, lc=#{lc}"
prefix << c
break unless lc.key?(c) && lc[c] > 0
lc[c] -= 1
ltrs_used += 1
end
puts " for w = #{w} prefix = #{prefix}"
if ltrs_used == word_len
puts " w = '#{w}' a match"
prefix = ''
true
else
puts " w = '#{w} *not* a match"
false
end
end
end
Alternative when word is relatively short
If the number of unique permutations of the letters of word is relatively small we could generate each word that is formed by a unique permutation of the characters in word and then use Array#bsearch to perform a binary search to determine if that word is in the dictionary:
def present?(word, dict)
dict.bsearch { |w| w >= word } == word
end
word.chars.permutation.map(&:join).uniq.select { |w| present?(w, dict) }
#=> ["aab", "aba"]

Using regular expressions to multiply and sum numeric string characters contained in a hash of mixed numeric strings

Without getting too much into biology, Proteins are made of Amino Acids. Each of the 20 Amino Acids that make up Proteins are represented by characters in a sequence. Each Amino Acid char has a different chemical formula, which I represent as strings. For example, "M" has a formula of "C5H11NO2S"
Given the 20 different formulas (and the varying frequency of each amino acid chars in a protein sequence) I want to compile all 20 of them into a single formula that will yield the total formula for the protein.
So first: multiply each formula by the frequency of its char in the sequence
Second : sum together all multiplied formulas into one formula.
To accomplish this, I first tried multiplying each amino acid char frequency in the sequence by the numbers in the chemical formula. I did this using .tally
sequence ="MGAAARTLRLALGLLLLATLLRPADACSCSPVHPQQAFCNADVVIRAKAVSEKEVDSGNDIYGNPIKRIQYEIKQIKMFKGPEKDIEFI"
sequence.chars.string.tally --> {"M"=>2, "G"=>5, "A"=>11, "R"=>5, "T"=>2, "L"=>9, "P"=>5, "D"=>5, "C"=>3, "S"=>4, "V"=>5, "H"=>1, "Q"=>4, "F"=>3, "N"=>3, "I"=>8, "K"=>7, "E"=>5, "Y"=>2}
Then, I listed all the amino acids chars and formulas into a hash
hash_of_formulas = {"A"=>"C3H7NO2", "R"=>"C6H14N4O2", "N"=>"C4H8N2O3", "D"=>"C4H7NO4", "C"=>"C3H7NO2S", "E"=>"C5H9NO4", "Q"=>"C5H10N2O3", "G"=>"C2H5NO2", "H"=>"C6H9N3O2", "I"=>"C6H13NO2", "L"=>"C6H13NO2", "K"=>"C6H14N2O2", "M"=>"C5H11NO2S", "F"=>"C9H11NO2", "P"=>"C5H9NO2", "S"=>"C3H7NO3", "T"=>"C4H9NO3", "W"=>"C11H12N2O2", "Y"=>"C9H11NO3", "V"=>"C5H11NO2"}
An example of what the process for my overall goal is:
In the sequence , "M" occurs twice so "C5H11NO2S" will become "C10H22N2O4S2". "C" has a formula of "C3H7NO2S" occurs 3 times: In the sequence so "C3H7NO2S" becomes "C9H21N3O6S3"
So, Summing together "C10H22N2O4S2" and "C9H21N3O6S3" will yield "C19H43N5O10S5"
How can I repeat the process of multiplying each formula by its frequency and then summing together all multiplied formulas?
I know that I could use regex for multiplying a formula by its frequency for an individual string using
formula_multiplied_by_frequency = "C5H11NO2S".gsub(/\d+/) { |x| x.to_i * 4}
But I'm not sure of any methods to use regex on strings embedded within hashes
If I understand correctly, you want the to provide the total formula for a given protein sequence. Here's how I'd do it:
NUCLEOTIDES = {"A"=>"C3H7NO2", "R"=>"C6H14N4O2", "N"=>"C4H8N2O3", "D"=>"C4H7NO4", "C"=>"C3H7NO2S", "E"=>"C5H9NO4", "Q"=>"C5H10N2O3", "G"=>"C2H5NO2", "H"=>"C6H9N3O2", "I"=>"C6H13NO2", "L"=>"C6H13NO2", "K"=>"C6H14N2O2", "M"=>"C5H11NO2S", "F"=>"C9H11NO2", "P"=>"C5H9NO2", "S"=>"C3H7NO3", "T"=>"C4H9NO3", "W"=>"C11H12N2O2", "Y"=>"C9H11NO3", "V"=>"C5H11NO2"}
NUCLEOTIDE_COMPOSITIONS = NUCLEOTIDES.each_with_object({}) { |(nucleotide, formula), compositions|
compositions[nucleotide] = formula.scan(/([A-Z][a-z]*)(\d*)/).map { |element, count| [element, count.empty? ? 1 : count.to_i] }.to_h
}
def formula(sequence)
sequence.each_char.with_object(Hash.new(0)) { |nucleotide, final_counts|
NUCLEOTIDE_COMPOSITIONS[nucleotide].each { |element, element_count|
final_counts[element] += element_count
}
}.map { |element, element_count|
"#{element}#{element_count.zero? ? "" : element_count}"
}.join
end
sequence = "MGAAARTLRLALGLLLLATLLRPADACSCSPVHPQQAFCNADVVIRAKAVSEKEVDSGNDIYGNPIKRIQYEIKQIKMFKGPEKDIEFI"
p formula(sequence)
# => "C434H888N51O213S"
You can't use regexp to multiply things. You can use it to parse a formula, but then it's on you and regular Ruby to do the math. The first job is to prepare a composition lookup by breaking down each nucleotide formula. Once we have a composition hash for each nucleotide, we can iterate over a nucleotide sequence, and add up all the elements of each nucleotide.
BTW, tally is not particularly useful here, since tally will need to iterate over the sequence, and then you have to iterate over tally anyway — and there is no aggregate operation going on that can't be done going over each letter independently.
EDIT: I probably made the regexp slightly more complicated that it needs to be, but it should parse stuff like CuSO4 correctly. I don't know if it's an accident or not that all nucleotides are only composed of elements with a single-character symbol... :P )
Givens
We are given a string representing a protein comprised of amino acids:
sequence = "MGAAARTLRLALGLLLLATLLRPADACSCSPVHPQQAFCNADVVIR" +
"AKAVSEKEVDSGNDIYGNPIKRIQYEIKQIKMFKGPEKDIEFI"
and a hash that contains the formulas of amino acids:
formulas = {
"A"=>"C3H7NO2", "R"=>"C6H14N4O2", "N"=>"C4H8N2O3", "D"=>"C4H7NO4",
"C"=>"C3H7NO2S", "E"=>"C5H9NO4", "Q"=>"C5H10N2O3", "G"=>"C2H5NO2",
"H"=>"C6H9N3O2", "I"=>"C6H13NO2", "L"=>"C6H13NO2", "K"=>"C6H14N2O2",
"M"=>"C5H11NO2S", "F"=>"C9H11NO2", "P"=>"C5H9NO2", "S"=>"C3H7NO3",
"T"=>"C4H9NO3", "W"=>"C11H12N2O2", "Y"=>"C9H11NO3", "V"=>"C5H11NO2"
}
Obtain counts of atoms in each amino acid
As a first step we can calculate the numbers of each atom in each amino acid:
counts = formulas.transform_values do |s|
s.scan(/[CHNOS]\d*/).
each_with_object({}) do |s,h|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
end
#=> {"A"=>{"C"=>3, "H"=>7, "N"=>1, "O"=>2},
# "R"=>{"C"=>6, "H"=>14, "N"=>4, "O"=>2},
# ...
# "M"=>{"C"=>5, "H"=>11, "N"=>1, "O"=>2, "S"=>1}
# ...
# "V"=>{"C"=>5, "H"=>11, "N"=>1, "O"=>2}}
Compute formula for protein
Then it's simply:
def protein_formula(sequence, counts)
sequence.each_char.
with_object("C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0) do |c,h|
counts[c].each { |aa,cnt| h[aa] += cnt }
end.each_with_object('') { |(aa,nbr),s| s << "#{aa}#{nbr}" }
end
protein_formula(sequence, counts)
#=> "C434H888N120O213S5"
Another example:
protein_formula("MCMPCFTTDHQMARKCDDCCGGKGRGKCYGPQCLCR", count)
#=> "C158H326N52O83S11"
Explanation of calculation of counts
This calculation:
counts = formulas.transform_values do |s|
s.scan(/[CHNOS]\d*/).each_with_object({}) do |s,h|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
end
uses the method Hash#transform_values. It will return a hash having the same keys as the hash formulas, with the values of those keys in formula modified by transform_values's block. For example, formulas["A"] ("C3H7NO2") is "transformed" to the hash {"C"=>3, "H"=>7, "N"=>1, "O"=>2} in the hash that is returned, counts.
transform_values passes each value of formulas to the block and sets the block variable equal to it. The first value passed is "C3H7NO2", so it sets:
s = "C3H7NO2"
We can write the block calculation more simply:
h = {}
s.scan(/[CHNOS]\d*/).each do |s|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
h
(Once you understand this calculation, which I explain below, see Enumerable#each_with_object to understand why I used that method in my solution.)
After initializing h to an empty hash, the following calculations are performed:
h = {}
a = s.scan(/[CHNOS]\d*/)
#=> ["C3", "H7", "N", "O2"]
a is computed using String#scan with the regular expression /[CHNOS]\d*/. That regular expression, or regex, matches exactly one character in the character class [CHNOS] followed by zero of more (*) digits (\d). It therefore separates the string "C3H7NO2" into the substrings that are returned in the array shown under the calculation of a above . Continuing,
a.each do |s|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
changes h to the following:
h #=> {"C"=>3, "H"=>7, "N"=>1, "O"=>2}
The block variable s is initially set equal to the first element of a that is passed to each's block:
s = "C3"
then we compute:
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
h["A"] = 2 == 1 ? 1 : "3".to_i
= false ? 1 : 3
3
This is repeated for each element of a.
Exclamation of construction of formula for the protein
We can simplify the following code1:
sequence.each_char.with_object("C"=>0, "H"=>0, "N"=>0, "O"=>0) do |c,h|
counts[c].each { |aa,cnt| h[aa] += cnt }
end.each_with_object('') { |(aa,nbr),s| s << "#{aa}#{nbr}" }
to more or less the following:
h = { "C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0 }
ch = sequence.chars
#=> ["M", "G", "A",..., "F", "I"]
ch.each do |c|
counts[c].each { |aa,cnt| h[aa] += cnt }
end
h #=> {"C"=>434, "H"=>888, "N"=>120, "O"=>213, "S"=>5}
When the first value of ch ("M") is passed to each's block (when h = { "C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0 }), the following calculations are performed:
c = "M"
g = counts[c]
#=> {"C"=>10, "H"=>22, "N"=>2, "O"=>4, "S"=>1}
g.each { |aa,cnt| h[aa] += cnt }
h #=> {"C"=>10, "H"=>22, "N"=>2, "O"=>4, "S"=>1}
Lastly, (when h #=> {"C"=>434, "H"=>888, "N"=>120, "O"=>213, "S"=>5})
s = ''
h.each { |aa,nbr| s << "#{aa}#{nbr}" }
s #=> "C434H888N120O213S5"
When aa = "C" and nbr = 434,
"#{aa}#{nbr}"
#=> "C434"
is appended to the string s.
1. (("C"=>0, "H"=>0, "N"=>0, "O"=>0) is shorthand for ({"C"=>0, "H"=>0, "N"=>0, "O"=>0}).

Ruby how to return index of a pair

Today I got a task with given array and 'target' which is a sum of 2 integers within that list. After some time I came out with draft solution but it does not seem to be passing all of the tests. Algorithm seems to be considering integer at [0] twice.
def two_sum(numbers, target)
numbers.combination 2 do |a, b|
if a + b == target
return numbers.index(a), numbers.index(b)
end
end
end
print two_sum([1, 2, 3], 4) # Expected [0, 2] *OK
print two_sum([1234, 5678, 9012], 14690) # Expected [1, 2] *OK
print two_sum([2, 2, 3], 4) # Expected [0, 1]) but I get [0, 0]
I have tried to use .map first instead of .combination(2) method but with the exact same result :-/
def two_sum(numbers, target)
[*0..numbers.size-1].combination(2).find { |i,j| numbers[i] + numbers[j] == target }
end
two_sum([1234, 5678, 9012], 14690)
#=> [1, 2]
two_sum([1234, 5678, 9012], 14691)
#=> nil
Here is a more efficient method that may prove useful when the arrays are large.
require 'set'
def two_sum(arr, target)
if target.even?
half = target/2
first = arr.index(half)
if first
last = arr.rindex(half)
return [first, last] unless last.nil? || first == last
end
end
a1, a2 = arr.uniq.partition { |n| n <= target/2 }
s = a2.to_set
n = a1.find { |n| s.include?(target-n) }
n.nil? ? nil : [arr.index(n), arr.index(target-n)]
end
If target is even I first check to see if one-half of it appears at least twice in arr. If so, we are finished (except for determining and returning the associated indices). Even if the method does not terminate after this step it is required this step does not result in an early termination it is required before the next steps are performed.
If target is odd or is even but one-half of it appears less than twice in arr I construct a temporary array that contains unique values in arr and then partition that into two arrays, a1, containing values no greater than target/2 and a2, containing values greater than target/2. It follows that if two numbers sum to target one must be in a1 and the other must be in a2.
To speed calculations I then convert a2 to a set s, and then loop through a1 looking for a value n such that s contains target-n. Let's try it.
arr = 100_000.times.map { rand(1_000_000) }
puts "target i1 arr[i1] i2 arr[i2] calc time (secs)"
puts "---------------------------------------------------------"
1000.times do
t = Time.now
target = rand(1_000_000)
i1, i2 = two_sum(arr, target)
print "#{target} -> "
print i1.nil? ? "nil " :
"#{i1} #{arr[i1]} #{i2} #{arr[i2]}"
puts " #{(Time.now-t).round(4)} secs"
end
prints
target i1 arr[i1] i2 arr[i2] calc time (secs)
---------------------------------------------------------
215113 -> 41 90943 11198 124170 0.027
344479 -> 0 78758 63570 265721 0.0237
188352 -> 190 79209 39912 109143 0.0275
457 -> nil 0.0255
923135 -> 78 84600 43928 838535 0.0207
59391 -> 2 5779 5454 53612 0.0289
259142 -> 73 58864 29278 200278 0.0284
364486 -> 8049 182243 89704 182243 0.001
895164 -> 13 205843 7705 689321 0.0228
880575 -> 20 440073 6195 440502 0.021
We see that arr does not contain two numbers that sum to 457. Also, notice the very short time in the antepenultimate row. That's because one-helf of target (364486/2 #=> 182243) appears at least twice in arr.

Longest palindrome within a string

I am supposed to return the size of the largest palindrome within a given string. For example, if I pass "racecar", I should get a return of 7. If I pass "racecarveryfast" or "veryfastracecar", it should still return 7. Specs I have to pass are:
Test.assert_equals(longest_palindrome("a"), 1)
Test.assert_equals(longest_palindrome("aa"), 2)
Test.assert_equals(longest_palindrome("baa"), 2)
Test.assert_equals(longest_palindrome("aab"), 2)
Test.assert_equals(longest_palindrome("baabcd"), 4)
Test.assert_equals(longest_palindrome("baablkj12345432133d"), 9)
and I am passing the first four with this code:
def longest_palindrome s
sub_count = 0
palidrome_count = []
s_array = s.chars
puts "string: " + s
puts "string array: " + s_array.to_s
if s.reverse == s
return s.size
else
s.match('(.)\1')[0].size
end
end
My thought process from here is breaking apart the string into smaller chunks, maybe through a loop. Any help or guidance would be appreciated.
def longest_palindrome(string)
i = 0
a = []
while !string[i..-1].empty?
j = -1
while !string[i..j].empty?
s = string[i..j]
if s.reverse == s
a << s.length
end
j -= 1
end
i += 1
end
a.max
end
Suppose the string has n characters. First see if the entire string is a palindrome. If it is, return the string. Fini! If not, see if either of the two substrings of length n-1 is a palindrome. If one is, return it. If not, examine substrings of length n-2, and so on. As long as the string contains at least one letter, the longest palindrome will be found.
def longest_palindrome(str)
arr = str.downcase.chars
str.length.downto(1) do |n|
ana = arr.each_cons(n).find { |b| b == b.reverse }
return ana.join if ana
end
end
The key method here is Enumerable#each_cons.
Here are some examples1:
longest_palindrome "a" #=> "a"
longest_palindrome "aa" #=> "aa"
longest_palindrome "baa" #=> "aa"
longest_palindrome "aab" #=> "aa"
longest_palindrome "baabcd" #=> "baab"
longest_palindrome "baablkj12345432133d" #=> "123454321"
longest_palindrome "I heard tattarrattats" #=> "tattarrattat"
1 James Joyce coined the word "tattarrattat" in Ulysses, to mean a knock on the door.

pythagorean triangle with any side equal to zero

So I have this spec I'm trying to solve for (I did not write the spec)...
it "returns false if any of the arguments are 0" do
# [0, 1, 1].permutation(3) returns all permutations of [0, 1, 1]
length = rand(0.01..100.0)
[0, length, length].permutation(3).all? { |(a,b,c)| valid_triangle?(a,b,c) }.should be_false
end
Here is my code
def valid_triangle?(a, b, c)
#A Pythagorean triple consists of three positive integers a, b, and c, such that a2 + b2 = c2.
if a > 0 && b > 0 && c > 0
one = a**2
two = b**2
three = c**2
if one+two=three
return "true"
else
return "false"
end
else
return "false"
end
end
How can I pass my spec? What am I missing?
The main problem is that when the test permutes the values of a, b and c, your method is not always checking that it is the square of the hypotenuse that equals the sum of the squares of the other two sides. For example, if a=3, b=4 and c=5, one of your tests will be 4*4 + 5*5 == 3*3. You need to sort a, b and c before checking the sum of squares, which is a good idea anyway, since the position of the hypotenuse among the parameters is not guaranteed. You could also simplify your code somewhat. Here's one way you could write it:
TOLERANCE = 0.01
def right_triangle?(a, b, c)
return false if a == 0 || b == 0 || c == 0
a, b, c = [a,b,c].sort
(a**2 + b**2 - c**2).abs < TOLERANCE
end
length = rand(0.01..100.0)
[0.0, length, length].permutation(3).all? { |(a,b,c)| right_triangle?(a,b,c)}
#=> false
[3,4,5].permutation(3).all? { |(a,b,c)| right_triangle?(a,b,c) }
#=> true
Since you are dealing with floats, you need to establish some level of tolerance when comparing values for equality. I've used an arbitrary fixed amount (0.01) for demonstration purposes.
You are returning "false" and "true" instead of false and true, also you check that one+two=three, when you should check one+two==three (an equals check, not an assignment)
def valid_triangle?(a, b, c)
#A Pythagorean triple consists of three positive integers a, b, and c, such that a2 + b2 = c2.
if a > 0 && b > 0 && c > 0
one = a**2
two = b**2
three = c**2
if one+two == three
return true
else
return false
end
else
return false
end
end

Resources