Related
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.
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!
I need to render the contents of the first 5 elements of an array and display "And X more" on a web page. Is there a built-in method on Array (or Enumerable) that easily separates one array into two sub-arrays: the first consisting of up to a fixed size and the second consisting of the array remainder?
I'm looking for one simple method call that will do this for me. Most of the methods that I looked at (like Enumerable#partition) use a logical condition to divide the array and don't supply the index to the block.
I just wrote the following code to do what I want. Please save me from myself and direct me to a method that already does it.
class Array
def bifurcate(size=length)
if size < 0
raise ArgumentError, "attempt to bifurcate using negative size"
end
remainder_size = length - size
if remainder_size < 0
remainder_size = 0
end
[
first(size),
last(remainder_size)
]
end
end
('a'..'g').to_a.bifurcate(2)
# => [["a", "b"], ["c", "d", "e", "f", "g"]]
('a'..'g').to_a.bifurcate(20)
# => [["a", "b", "c", "d", "e", "f", "g"], []]
('a'..'g').to_a.bifurcate()
# => [["a", "b", "c", "d", "e", "f", "g"], []]
('a'..'g').to_a.bifurcate(0)
# [[], ["a", "b", "c", "d", "e", "f", "g"]]
('a'..'g').to_a.bifurcate(-1)
# ArgumentError: attempt to bifurcate using negative size
Also, let me qualify that I want one simple method call to do what I want. Also consider that the starting array may contain duplicate values and this method needs to respect the original array and return duplicates.
You can use Enumerable#partition along with Enumerator#with_index method, as shown below:
size = 2
(1..6).partition.with_index { |_,i| i < size }
#=> [[1, 2], [3, 4, 5, 6]]
Alternatively, if your input array can be mutated, then, following will also do the trick
[array.shift(size), array]
[array.take(3), array.drop(3)]
# [["a", "b", "c"], ["d", "e", "f", "g"]]
Hope it helps :)
Use Array#[]:
[arr[0,size_of_first], arr[size_of_first..-1] || []]
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]
I think its' silly question lol
I have below array
[['a','b','c'],['d','e','f']]
and want that array to be
['a','b','c'],['d','e','f']
which means i want to remove the first bracket.
Does that make sense?
Thanks in adv.
no, this doesn't make sense really, because ['a','b','c'],['d','e','f'] in this notation are two separate objects/arrays not inside any other data structure...
you could do an assignment, like :
a,b = [['a','b','c'],['d','e','f']]
and then
> a
=> ["a", "b", "c"]
> b
=> ["d", "e", "f"]
or better just iterate over the outer array (because you don't know how many elements it has):
input = [['a','b','c'],['d','e','f']]
input.each do |x|
puts "element #{x.inspect}"
end
=>
element ["a", "b", "c"]
element ["d", "e", "f"]
It doesn’t make sense. Do you mean a string manipulation?
irb(main):001:0> s = "[['a','b','c'],['d','e','f']]"
=> "[['a','b','c'],['d','e','f']]"
irb(main):002:0> s[1...-1]
=> "['a','b','c'],['d','e','f']"
Or, do you want to flatten an array?
irb(main):003:0> [['a','b','c'],['d','e','f']].flatten
=> ["a", "b", "c", "d", "e", "f"]