Related
I want to create an array of possibly repeated combinations of n elements from the letters A to F. Each combination should be an array. For n = 4 for example, the result should be:
[[A, A, A, A], [A, A, A, B], [A, A, A, C], ..., [F, F, F, F]]
without significance in order among the elements ([A, A, E, E] and [E, E, A, A] would be the same).
I tried it with:
('A'..'F').to_a.combination(4).to_a
but that didn't work. I guess the task is more complicated.
Try repeated_combination:
('A'..'F').to_a.repeated_combination(4).to_a
Example:
> ('A'..'C').to_a.repeated_combination(4).to_a
=> [["A", "A", "A", "A"], ["A", "A", "A", "B"], ["A", "A", "A", "C"], ["A", "A", "B", "B"], ["A", "A", "B", "C"], ["A", "A", "C", "C"], ["A", "B", "B", "B"], ["A", "B", "B", "C"], ["A", "B", "C", "C"], ["A", "C", "C", "C"], ["B", "B", "B", "B"], ["B", "B", "B", "C"], ["B", "B", "C", "C"], ["B", "C", "C", "C"], ["C", "C", "C", "C"]]
By "upgrouped" I mean elements that are uniq to its left and its right elements.
Example:
arr = ["a", "a", "a", "b", "a", "c", "c", "d", "b"]
We can group arr like this [["a", 3], ["b, 1], ["a", 1], ["c", 2], ["d", 1], ["b", 1]]. Let's say once a letter is "grouped" we are not going to pick any other ungrouped instances of that letter.
So our final answer would be arr = ["b", "d"]
What could be a great option to replace the "regex" part in arr.select{|chr| chr(/\regex/)} (if there is no other ways)?
Edit: added "b" in the end of array. arr.count("b") == 2, but since "b" is ungrouped, it is still part of the return array.
You could do it in two steps without regex:
banned = arr.chunk(&:itself).select { |e| e.last.size > 1 }.map(&:first)
#=> ["a", "c"]
arr.uniq - banned
#=> ["b", "d"]
Basically first we get the list of elements that are grouped and need to be rejected, and then remove those elements from original array.
Here is a step by step example (for banned list):
arr.chunk(&:itself).to_a #=> [["a", ["a", "a", "a"]], ["b", ["b"]], ["a", ["a"]], ["c", ["c", "c"]], ["d", ["d"]], ["b", ["b"]]]
.select { |e| e.last.size > 1 } #=> [["a", ["a", "a", "a"]], ["c", ["c", "c"]]]
.map(&:first) #=> ["a", "c"]
Notice that to_a is added only to show the content of the Enumerable that results from chunk, but it is not used in the final solution since map can be executed on any Enumerable object.
Another option to get banned list could be using each_with_object:
arr.chunk(&:itself).each_with_object([]) { |elem, result| result << elem[0] if elem[1].size > 1 }
#=> ["a", "c"]
This will only iterate arr twice (first option will iterate it 3 times).
I don't know how I'd use a regular expression for this problem, but the method Enumerable#each_cons can be used to advantage.
Let grouped and ungrouped respectively be arrays of elements of a given array arr, where grouped are the grouped elements and ungrouped are the ungrouped elements. Every element is grouped or ungrouped.
My understanding of the question is that we wish to construct an array
(ungrouped - grouped).uniq
where Array#- is array difference.
It is clear that, for i=1,..., arr.size-2, arr[i] is ungrouped if arr[i-1] != arr[i] && arr[i] != arr[i+1]. It's not evident, however, how one determines whether the first and last elements of an array are ungrouped. The definition states that, to be ungrouped, the element must differ from both of it's adjacent elements. Since the first and last elements only have one adjacent element, it follows that the first and last elements are never ungrouped.
The alternative interpretation is that an element is ungrouped if it differs from all of its adjacent elements (that being one for the first and last and two for all others). I will consider these two interpretations separately. For both,
arr = [["a", 3], ["b", 1], ["a", 1], ["c", 2], ["d", 1], "b"]
Ungrouped elements must differ from both of their adjacent elements
ungrouped, grouped = [arr.first, *arr, arr.last].each_cons(3).
with_object([[],[]]) do |(p,c,n), (ungrouped, grouped)|
(c==p || c==n ? grouped : ungrouped) << c
end
(ungrouped - grouped).uniq
# => ["d"]
Ungrouped elements must differ from all of their adjacent elements
ungrouped, grouped = [nil, *arr, nil].each_cons(3).
with_object([[],[]]) do |(p,c,n), (ungrouped, grouped)|
(c==p || c==n ? grouped : ungrouped) << c
end
(ungrouped - grouped).uniq
# => ["b", "d"]
This assumes that no elements of arr equal nil.
The steps for this second interpretation are as follows.
a = [nil, *arr, nil]
#=> [nil, "a", "a", "a", "b", "a", "c", "c", "d", "b", nil]
b = a.each_cons(3)
#=> #<Enumerator: [nil, "a", "a",..., "d", "b", nil]:each_cons(3)>
We can see the elements that will be generated by this enumerator by converting it to an array.
b.to_a
#=> [[nil, "a", "a"], ["a", "a", "a"], ["a", "a", "b"], ["a", "b", "a"],
# ["b", "a", "c"], ["a", "c", "c"], ["c", "c", "d"], ["c", "d", "b"],
# ["d", "b", nil]]
Continuing,
d = b.with_object([[],[]])
#=> #<Enumerator: #<Enumerator: [nil, "a", "a",..., "d", "b", nil]:
# each_cons(3)>:with_object([[], []])>
d.to_a
#=> [[[nil, "a", "a"], [[], []]], [["a", "a", "a"], [[], []]],
# [["a", "a", "b"], [[], []]], [["a", "b", "a"], [[], []]],
# [["b", "a", "c"], [[], []]], [["a", "c", "c"], [[], []]],
# [["c", "c", "d"], [[], []]], [["c", "d", "b"], [[], []]],
# [["d", "b", nil], [[], []]]]
If one examines the return value for the construction of this enumerator, you can see it can be thought of as a compound enumerator.
ungrouped, grouped = d.each do |(p,c,n), (ungrouped, grouped)|
(c==p || c==n ? grouped : ungrouped) << c
end
#=> [["b", "a", "d", "b"], ["a", "a", "a", "c", "c"]]
ungrouped
#=> ["b", "a", "d", "b"]
grouped
#=> ["a", "a", "a", "c", "c"]]
e = ungrouped - grouped
#=> ["b", "d", "b"]
e.uniq
#=> ["b", "d"]
Let's take a closer look at the calculation of ungrouped and grouped. The first element of the enumerator d is passed to the block and the block variables are assigned values.
(p,c,n), (ungrouped, grouped) = d.next
#=> [[nil, "a", "a"], [["b", "a", "d", "b"], ["a", "a", "a", "c", "c"]]]
p
#=> nil
c
#=> "a"
n
#=> "a"
ungrouped
#=> []
grouped
#=> []
c==p || c==n
#=> "a"==nil || "a"=="a"
#=> true
Therefore,
grouped << c
#=> ["a"]
The remaining calculations are similar, as are the calculations under the first assumption concerning the first and last elements of the array.
How to combine 2 arrays like this
a = ["x","y","z"]
b = [["a","b"],["c","d"],["e","f"]]
expected output:
[["a","b","x" ],["c","d","y"],["e","f","z"]]
Is there any inbuilt method?
There is. You can use Array#zip in conjunction with Array#flatten:
b.zip(a).map(&:flatten)
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
another way is:
[b, a].transpose.map(&:flatten)
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
:)
Here is one more way to do this:
a = ["x","y","z"]
b = [["a","b"],["c","d"],["e","f"]]
b.map.with_index {|arr, idx| arr << a[idx]}
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
enum = a.to_enum
b.map { |arr| arr << enum.next }
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
Say i have the following array:
array = ["a", "b", "c", "new", "d", "e", "f", "g", "new", "h", "i", "new"]
I want to create a new array every time I find the value "new".
So I would end up with:
array1 = ["a", "b", "c"]
array2 = ["d", "e", "f", "g"]
array3 = ["h", "i"]
What would be the best way to do this?
As the name implies you can split arrays with the split function, however that returns ArraySlice objects.
To get arrays back from the slices you need to map them (credits to Martin R)
let array = ["a", "b", "c", "new", "d", "e", "f", "g", "new", "h", "i", "new"]
let splittedArrays = array.split(separator: "new").map(Array.init)
The code below currently pushes a copy of #startyear into #new. I need to convert this into 1 single array, any ideas?
Forums didn't have much
startyear = [["a", "b", "z"], ["c", "d"], ["e", "f"], ["g", "h", "i", "j"]]
new = []
startyear.each do |n| #.transpose here?
puts "looping #{n}"
new.push(n)
#n.join is needed somewhere
puts "#{startyear.length} is the length of startyear"
break if startyear.length == startyear.length[4]
end
puts "your new array is : #{new}"
You can use Array#flatten:
startyear = [["a", "b", "z"], ["c", "d"], ["e", "f"], ["g", "h", "i", "j"]]
flattened = startyear.flatten
# flattened is now ["a", "b", "z", "c", "d", "e", "f", "g", "h", "i", "j"]
Array#flatten is the obvious method to use here, but as is generally the case with Ruby, there are alternatives. Here are two.
Use Enumerable#flat_map and Object#itself
startyear.flat_map(&:itself)
#=> ["a", "b", "z", "c", "d", "e", "f", "g", "h", "i", "j"]
itself was introduced in Ruby v2.2. For earlier versions, use:
startyear.flat_map { |a| a }
Use Enumerable#reduce (aka inject)
startyear.reduce(:+)
#=> ["a", "b", "z", "c", "d", "e", "f", "g", "h", "i", "j"]