Ruby longest palindrome. Nested while loops - arrays

I am writing some code to find the longest palindrome in a string. I want to start at index 0 and then push the increasing length of the substring to an array:
ex:
string = "ababa"
[["a", "b"], ["a", "b", "a"], ["a", "b", "a", "b"], ["a", "b", "a", "b", "a"]]
It should then start on index 1 and do the same:
ex:
string = "ababa"
[["b","a"],["b","a","b"],["b","a","b","a"]
This should continue until the index is length -1 .However, the following code stops after it has gone through all iterations beginning with the first index and only returns:
[["a", "b"], ["a", "b", "a"], ["a", "b", "a", "b"], ["a", "b", "a", "b", "a"]]
What is the flaw in my logic? Code below
def longest_palindrome(s)
array = s.chars
start = 0
place = 1
output = []
while start < s.length - 1
while place < s.length
output << array[start..place]
place += 1
end
start += 1
end
return output
end

I believe this is what you are after:
def longest_palindrome(s)
arr = s.chars
output = []
(0...s.length).each do |start|
(start + 1...s.length).each do |place|
output << arr[start..place]
end
end
output
end
longest_palindrome("ababa")
=> [["a", "b"], ["a", "b", "a"], ["a", "b", "a", "b"], ["a", "b", "a", "b", "a"], ["b", "a"], ["b", "a", "b"], ["b", "a", "b", "a"], ["a", "b"], ["a", "b", "a"], ["b", "a"]]
Using iterators really simplifies things. Here is a more concise version:
def longest_palindrome(s)
output = (0...s.length).flat_map do |start|
(start + 1...s.length).map do |place|
s[start..place]
end
end
end
longest_palindrome("ababa")
=> ["ab", "aba", "abab", "ababa", "ba", "bab", "baba", "ab", "aba", "ba"]

A working, non-optimized, written on-the-fly 30 minutes, who knows how awfully slow longest palindrome finder:
def longest_palindrome(string, min_size = 2)
string = string.downcase # we will not be taking
return string if string == string.reverse # skip all calculaions of the passed string itself is a palindrome in original order
letters = string.chars # Convert string to array of bytes
combinations = [] # Initialize all letter combinations
(min_size..letters.size).each do |n| # min_size is the shortest length a palindrome is allowed to be, default 2
combinations.concat(letters.combination(n).to_a) # concat all combinations for n amount of characters in the string
end
palindromes = [] # Initialize array for all palindromes
combinations.each do |combo| # interate every combo
combo.size.times do # for every letter ordering is done via size of this letter combo
palindromes << combo.dup if combo == combo.reverse # add to list of palindromes if the combinations is the same backwards
combo.rotate! # rotate the letters for next order checking
end
end
palindromes.sort {|a, b| a.size <=> b.size }.last.join # sort the palidromes by length, take the biggest one, and return it as a full string
end
p longest_palindrome("racecar") #=> racecar
p longest_palindrome("applesauce") #=> pecep
p longest_palindrome("madam im adam") #=> mada m adam
p longest_palindrome("madamimadam") #=> madamimadam
but it works, sort of.
As you can see if sort of gets odd when spaces/puncuation are added to the mix.
You can always clean the string of it in the first line if need be.
Viva la algorithmless coding!

Related

How do I split an array into smaller arrays bsaed on a condition?

Ruby 2.4. I have an array of strings
2.4.0 :007 > arr = ["a", "b", "g", "e", "f", "i"]
=> ["a", "b", "g", "e", "f", "h", "i"]
How do I split my array into smaller arrays based on a condition? I have a function -- "contains_vowel," which returns true if a string contains "a", "e", "i", "o", or "u". How would I take an array of strings and split it into smaller arrays, using a divider function of "contains_vowel"? That is, for the above, the resulting array of smaller arrays would be
[["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
If an element of the larger array satisfies the condition, it would become an array of one element.
arr = ["a", "b", "g", "e", "f", "i"]
r = /[aeiou]/
arr.slice_when { |a,b| a.match?(r) ^ b.match?(r) }.to_a
=> [["a"], ["b", "g"], ["e"], ["f"], ["i"]]
String#match? made its debut in Ruby v2.4. For earlier versions you could use (for example) !!(b =~ r), where !! converts a truthy/falsy value to true/false. That converstion is needed because the XOR operator ^ serves double-duty: it's a logical XOR when a and b in a^b are true, false or nil, and a bit-wise XOR when the operands are integers, such as 2^6 #=> 4 (2.to_s(2) #=> "10"; 6.to_s(2) #=> "110"; 4.to_s(2) #=> "100").
One more way to skin a cat
def contains_vowel(v)
v.count("aeiou") > 0
end
def split_by_substring_with_vowels(arr)
arr.chunk_while do |before,after|
!contains_vowel(before) & !contains_vowel(after)
end.to_a
end
split_by_substring_with_vowels(arr)
#=> [["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
What it does:
passes each consecutive 2 elements
splits when either of them contain vowels
Example with your other Array
arr = ["1)", "dwr", "lyn,", "18,", "bbe"]
split_by_substring_with_vowels(arr)
#=> [["1)", "dwr", "lyn,", "18,"], ["bbe"]]
Further example: (if you want vowel containing elements in succession to stay in the same group)
def split_by_substring_with_vowels(arr)
arr.chunk_while do |before,after|
v_before,v_after = contains_vowel(before),contains_vowel(after)
(!v_before & !v_after) ^ (v_before & v_after)
end.to_a
end
arr = ["1)", "dwr", "lyn,", "18,", "bbe", "re", "rr", "aa", "ee"]
split_by_substring_with_vowels(arr)
#=> [["1)", "dwr", "lyn,", "18,"], ["bbe", "re"], ["rr"], ["aa", "ee"]]
This checks if before and after are both not vowels Or if they both are vowels
I might use chunk which splits an array everytime the value of its block changes. Chunk returns a list of [block_value, [elements]] pairs, I used .map(&:last) to only get the sub-lists of elements.
arr = ["a", "b", "g", "e", "f", "h", "i"]
def vowel?(x); %w(a e i o u).include?(x); end
arr.chunk{|x| vowel?(x)}.map(&:last)
=> [["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
contains_vowel = ->(str) { !(str.split('') & %w|a e i o u|).empty? }
_, result = ["a", "b", "g", "e", "f", "h", "i"].
each_with_object([false, []]) do |e, acc|
cv, acc[0] = acc[0], contains_vowel.(e)
cv ^ acc.first ? acc.last << [e] : (acc.last[-1] ||= []) << e
end
result
#⇒ [["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
What we do here:
contains_vowel is a lambda to check whether the string contains a vowel or not.
we reduce the input array, collecting the last value (contained the previously handled string the vowel or not,) and the result.
cv ^ acc.first checks whether it was a flip-flop of vowel on the last step.
whether is was, we append a new array to the result
whether is was not, we append the string to the last array in the result.

How to iterate over consecutive elements

I'm looking for a method that is similar to Array#combination, but the order matters.
Given this input:
array = ['a','b','c','d','e']
I'd like to get:
[['a','b','c'],['b','c','d'],['c','d','e']]
I'm trying to find the method that does this:
array = ['a','b','c','d','e']
x,a = 3,[]
until x > (ary.size) do
a += (0.upto(ary.size - x).map{|i| ary[i..(x-1)+i]} )
x += 1
end
The Enumerable documentation is your friend:
array = ['a','b','c','d','e']
array.each_cons(3).to_a
# => [["a", "b", "c"], ["b", "c", "d"], ["c", "d", "e"]]
each_cons(n) { ... }
Iterates the given block for each array of consecutive elements. If no block is given, returns an enumerator.

Delete array element if index position is greater than a specific value

I am trying to delete elements from an array if its index is greater than a certain value. I am looking to do something like this:
a = ["a", "b", "c"]
b = a.delete_if {|x| x.index > 1 }
I took a look at drop, delete_if, etc. I tried completing this using each_with_index like this:
new_arr = []
a.each_with_index do |obj, index|
if index > 1
obj.delete
end
new_arry << obj
end
How can I delete an array element if it's array position is greater than a certain value?
Here are some other ways to return a sans elements at indices >= index, which is probably better expressed as "returning the first index elements". All below return ["a", "b"]).
a = ["a", "b", "c", "d", "e"]
index = 2
Non-destructive (i.e., a is not altered)
a[0,index]
index.times.map { |i| a[i] }
Destructive (a is modified or "mutated")
a.object_id #=> 70109376954280
a = a[0,index]
a.object_id #=> 70109377839640
a.object_id #=> 70109377699700
a.replace(a.first(index))
a.object_id #=> 70109377699700
You can use slice! and give it a range. It is a destructive method as indicated by the !, so it will mutate your array.
a = [1, 2, 3, 4]
a.slice!(2..-1)
a = [1, 2]
Array#first gives you the first n elements.
b = a.first(1)
# => ["a"]
If you want to do it in a destructive way, then this will do:
a.pop(a.length - 1)
a # => ["a"]
You can append with_index:
a = ["a", "b", "c"]
a.delete_if.with_index { |x, i| i > 1 }
a #=> ["a", "b"]
Another example:
a = ("a".."z").to_a
a.delete_if.with_index { |x, i| i.odd? }
#=> ["a", "c", "e", "g", "i", "k", "m", "o", "q", "s", "u", "w", "y"]
Going by your question, "How can I delete an array element if it's array position is greater than a certain value?".
I assume what you want is that the final array you have should contain only elements before the specified index.
You can just do this:
your_array.select { |element| your_array.index(element) < max_index }
E.g
figures = [1,2,3,4,5,6]
figures.select{ |fig| figures.index(fig) < 3 }
# => [1, 2, 3]

Get array element values without square brackets and double quotes

I have a couple of Ruby arrays:
array1 = ["a", "b"]
array2 = ["a", "b", "c"]
array3 = ["a", "b", "c", "d"]
array4 = ["a", "b", "c", "d", "e"]
I need to return the following strings:
#array1
"a"
#array2
"a and b"
#array3
"a, b and c"
#array4
"a, b, c and d"
The last element of the array should never be displayed.
I don't know in advance how many elements an array contains or the value of these elements.
To achieve what I need, I came up with the following method:
def format_array(array)
if array.length - 1 == 1
array[0].to_s
elsif array.length - 1 == 2
array[0].to_s + " and " + array[1].to_s
elsif array.length - 1 > 2
array.sort.each_with_index do |key, index|
unless key == "e"
if index == array.length - 2
" and " + array[index].to_s
else
array[index].to_s + ", "
end
end
end
end
end
This method returns an arrays of values with square brackets and double quotes instead of lean strings. For instance, I get ["a", "b", "c", "d", "e"] instead of "a, b, c and d" for array4.
How can I make this work?
def join_with_commas_and_and(array)
if array.length <= 2
array.join(' and ')
else
[array[0..-2].join(', '), array[-1]].join(' and ')
end
end
EDIT: to ignore the last element, add this line as the first line in the function:
array = array[0..-2]
I think it's easiest to disregard 'and' until commas are inserted, then replace the last comma with 'and':
def fmt(arr)
return arr.first if arr.size == 2
str = arr[0..-2].join(', ')
str[str.rindex(',')] = ' and'
str
end
# ["a", "b"]: a
# ["a", "b", "c"]: a and b
# ["a", "b", "c", "d"]: a, b and c
# ["a", "b", "c", "d", "e"]: a, b, c and d

Nested loops in Ruby

I am trying to count the number of similar prefix beginnings to a string in Ruby. e.g; input "ababaa" should output 11;
ababaa = 6
babaa = 0
abaa = 3
baa = 0
aa = 1
a = 1
I have got as far as the code below, using a nested loop to go through each of the above as an array, however it looks as though Ruby is currently outputting the count of just the first Array object, "ababaa".
Solved, thanks :)
def string_suffix(string)
num = 0
ary = []
string.length.times do
ary << string[num..string.length]
num = num + 1
end
result = 0
ary.each do |x| # ["ababaa", "babaa", "abaa", "baa", "aa", "a"]
x.chars.each_with_index do |c,index|
break unless c == string[index]
result = result + 1
end
end
return result
end
I have looked far and wide and still cannot solve the issue, It looks like the (final, nested) array is breaking after the first iteration of the 'ary' Array and just returning that output.
You are returning the result while you are still in the loop. You need to move result = 0 out of the loop, and move the return result statement outside of the loop too. At the moment the function is going through the first iteration of the loop ("ababaa", for which all characters match), but you want result to equal the sum of all results.
Additionally, instead of doing:
count = 0
x.chars.each do |x|
if x == string[count]
count = count + 1
result = result + 1
else
count = count + 1
end
end
You could use the function each_with_index, to get
x.chars.each_with_index do |c,index|
if c == string[index]
result = result + 1
end
end
However, since you are trying to count how many characters in the substring are a prefix of string, you want to break when you first find a character c that is not equal to string[index], so that you don't end up counting extra characters. The loop then becomes:
x.chars.each_with_index do |c,index|
if c == string[index]
result = result + 1
else
break
end
end
I noticed you are returning the result inside your second loop, at the end. This means that after you've gone through the first item in your array the function returns just the result for the first item. Move your return statement to outside the loop.
As I understand, the problem is this: given a string s, for each i = 0..s.size-1, compute the number of leading characters of s[0..-i-1] that match the corresponding characters (i.e., at same offsets) of s[i..-1], and sum these s.size subtotals.
Here's a Ruby-like way to do that, using Enumerable#reduce (aka inject) and Enumerable#take_while:
str = "ababaa"
arr = str.chars
(0...arr.size).reduce(0) do |tot,i|
tot + arr[0..-i-1].zip(arr[i..-1]).take_while { |x,y| x == y }.size
end
#=> 11
The steps:
arr = str.chars
#=> ["a", "b", "a", "b", "a", "a"]
r = 0...arr.size
#=> 0...6
When the first element of r is passed to the block, the block variables are set to:
tot = 0
i = 0
The block calculation is therefore as follows:
a = arr[0..-i-1].zip(arr[i..-1])
#=> arr[0..-1].zip(arr[0..-1])
#=> arr.zip(arr)
#=> ["a", "b", "a", "b", "a", "a"].zip(["a", "b", "a", "b", "a", "a"])
#=> [["a", "a"], ["b", "b"], ["a", "a"], ["b", "b"], ["a", "a"], ["a", "a"]]
b = a.take_while { |x,y| x == y }
#=> [["a", "a"], ["b", "b"], ["a", "a"], ["b", "b"], ["a", "a"], ["a", "a"]]
tot + b.size
#=> 0 + 6
#=> 6
Note that this calculation will always equal arr.size for the first element of arr passed to the block.
When the next element of arr is passed to the block, the block variable i is set to 1. tot, which we just computed, equals 6. The block calculation is therefore:
a = arr[0..-i-1].zip(arr[i..-1])
#=> arr[0..-2].zip(arr[1..-1])
#=> ["a", "b", "a", "b", "a"].zip(["b", "a", "b", "a", "a"])
#=> [["a", "b"], ["b", "a"], ["a", "b"], ["b", "a"], ["a", "a"]]
b = a.take_while { |x,y| x == y }
#=> []
tot + b.size
#=> 6 + 0
#=> 6
The remaining calculations are similar. After all elements of arr have been sent to the block, reduce returns the value of tot.

Resources