Related
How to change something like this
array = ["a, b, c, d"]
to
array = ["a", "b", "c", "d"]
and vice versa
Use String#split and Array#join:
["a, b, c, d"].first.split(', ')
# => ["a", "b", "c", "d"]
[["a", "b", "c", "d"].join(', ')]
# => ["a, b, c, d"]
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.
In this code:
letters = %w{a b c d e f g}
letters_2 = letters
i = 0
while i < letters.length
letters[i] = "X"
puts "letters = " + letters.join
puts "letters_2 = " + letters_2.join
puts "======================"
i+=1
end
why does letters_2 array get changed when the code is only explicitly modifying the letters array?
In your example, there is only one array, and letters and letters_2 are both assigned to it.
It sounds like what you want is for letters_2 be a separate array, that's initialized from the letters array, but independent thereafter? If so, then one way to do that is to use the Array.new method:
letters = %w{a b c d e f g}
letters_2 = Array.new(letters)
# letters and letters_2 are now separate arrays with the same elements
letters[3] = "X" # this only affects letters, not letters_2
letters_2[4] = "Y" # this only affects letters_2, not letters
you are assigning the same reference there.
You can see here
letters = %w{a b c d e f g}
letters_2 = letters
puts letters.object_id #4579428
puts letters_2.object_id #4579428
So If you want to make the second one as separate array, then call 'dup' function when you assign the first array to the second,
letters = %w{a b c d e f g}
letters_2 = letters.dup
puts letters.object_id #18932832
puts letters_2.object_id #18932580
you define only one array letters
than you have assign letters to letters_2.so you create create same array letters_2 as letters .therefore any change made in letters will affect on letters_2.
both array refereeing same object_id
you can check that letters.object_id is same as letters_2.object_id
both array refer same memory address.
2.2.3 :038 > letters = %w{a b c d e f g}
=> ["a", "b", "c", "d", "e", "f", "g"]
2.2.3 :039 > letters_2 = letters
=> ["a", "b", "c", "d", "e", "f", "g"]
2.2.3 :040 > letters_2
=> ["a", "b", "c", "d", "e", "f", "g"]
2.2.3 :041 > letters
=> ["a", "b", "c", "d", "e", "f", "g"]
Suppose there is an array like this one:
list = ["a", "a", "a", "b", "b", "c", "d", "e", "e"]
We want to create a cycle where every next element is different from the previous element and the first element is different from the last element.
required = ["a", "b", "a", "b", "a", "c", "e", "d", "e"]
How is this done in ruby?
def create_cycle
temp = Array.new($input)
i, j, counter = 0
while i == 0
while (counter != $input.length)
j = rand(1..$input.length-1).floor
unless !($input[i][0].to_s.eql?$input[j][0])
$solution.push($input[i])
$solution.push($input[j])
puts input[i], input[j]
$input.delete_at(i)
$input.delete_at(j)
counter = counter + 1
end
end
end
end
I'm trying to learn this. Thank you for your help.
Additional notes:
The elements a, b, c, d, e represent special format strings, where
a certain property is common among them, thus the first element "a"
shares a property with the next element "a" but is not equivalent to
the first.
In the case it isn't possible to create a cycle, then, it is enough to raise a flag in command line.
I might do it like this:
>> list = [a, a, a, b, b, c, d, e, e]
>> list.sort.each_slice((list.size/2.0).round).reduce(:zip).flatten.compact
=> [a, c, a, d, a, e, b, e, b]
The general method is to:
sort the list, so all identical members are adjacent
divide the list in half from the middle
interleave the two halves together
Assuming you do not care about the order being the same as in the original array, and it is ok to have duplicates if there is no way, and also assuming the list is presorted, here is one approach - it just keeps adding elements from the beginning and end of the list till there are no elements left:
def interleaver list
result = []
el = list.first
while(el)
el = list.shift
if el
result << el
else
return result
end
el = list.pop
if el
result << el
else
return result
end
end
result
end
> a = 'a'
> b = 'b'
> c = 'c'
> d = 'd'
> e = 'e'
> list = [a, a, a, b, b, c, d, e, e]
> interleaver(list)
=> ["a", "e", "a", "e", "a", "d", "b", "c", "b"]
But if such interleaving is not possible, you will get duplicates:
> list = [a, a, a, b]
> interleaver(list)
#=> ["a","b","a","a"]
You can obtain such a string, or demonstrate that no such string exists, with the following recursive method.
Code
def doit(remaining, partial=[])
first_partial, last_partial = partial.first, partial.last
if remaining.size == 1
return ([first_partial, last_partial] & remaining).empty? ?
partial + remaining : nil
end
remaining.uniq.each_with_index do |s,i|
next if s == last_partial
rem = remaining.dup
rem.delete_at(i)
rv = doit(rem, partial + [s])
return rv if rv
end
nil
end
Examples
list = %w| a a b |
#=> ["a", "a", "b"]
doit list
#=> nil
The above demonstrates that the three elements of list cannot be permuted to satisfy the two ordering requirements.
list = %w| a a a b b c d e e |
#=> ["a", "a", "a", "b", "b", "c", "d", "e", "e"]
doit list
#=> ["a", "b", "a", "b", "c", "b", "e", "d", "e"]
This took 0.0042 second to solve on a newish MacBook Pro.
list = %w| a a a a a a a b b c d e e f f f g g g g h i i i i j j |
#=> ["a", "a", "a", "a", "a", "a", "a", "b", "b", "c", "d", "e", "e",
# "f", "f", "f", "g", "g", "g", "g", "h", "i", "i", "i", "i", "j", "j"]
doit list
#=> ["a", "b", "a", "b", "a", "b", "a", "b", "c", "b", "d", "e", "f",
# "e", "f", "g", "f", "g", "h", "g", "h", "i", "j", "i", "j", "i", "j"]
This took 0.0059 seconds to solve.
Out of curiosity, I then tried
list = (%w| a a a a a a a b b c d e e f f f g g g g h i i i i j j |).shuffle
#=> ["a", "c", "f", "b", "d", "i", "a", "a", "i", "a", "a", "g", "g",
# "a", "g", "i", "j", "b", "h", "j", "e", "e", "a", "g", "f", "i", "f"]
doit list
#=> ["a", "c", "f", "b", "d", "i", "a", "i", "a", "g", "a", "g", "a",
# "g", "i", "g", "j", "b", "h", "j", "e", "a", "e", "g", "f", "i", "f"]
This took a whooping 1.16 seconds to solve, suggesting that it may be desirable to pre-sort list (doit(list.sort)) if, of course, list is sortable.
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