How to add two multidimensional arrays - arrays

I want to do the following:
array1 = [[1, 10], [2, 20], [3, 10], [4, 30]]
array2 = [[1, 10], [2, 10], [3, 5], [4, 10]]
I want to add two arrays in such a way that the second element of each subarray will be added. I want the following output.
result = [[1,20],[2,30],[3,15],[4,40]]

[array1, array2].transpose.map{|(k, v1), (_, v2)| [k, v1 + v2]}
# => [[1, 20], [2, 30], [3, 15], [4, 40]]

Another approach as below :
array1 = [[1,10],[2,20],[3,10],[4,30]]
array2 = [[1,10],[2,10],[3,5],[4,10]]
Hash[array1].merge(Hash[array2]) { |key,old,new| old + new }.to_a
# => [[1, 20], [2, 30], [3, 15], [4, 40]]
Taking the help of merge(other_hash){|key, oldval, newval| block} .

This can be achieved with a combination of Array#zip and Array#map:
result = array1.zip(array2).map { |l, r| [l[0], l[1] + r[1]] }
#=> [[1, 20], [2, 30], [3, 15], [4, 40]]
However, key-value pairs are often best treated as a Hash. Among other operations, this will allow you to #merge:
hash1
#=> {1=>10, 2=>20, 3=>10, 4=>30}
hash2
#=> {1=>10, 2=>10, 3=>5, 4=>10}
result = hash1.merge(hash2) { |_, l, r| l + r }
#=> {1=>20, 2=>30, 3=>15, 4=>40}

Related

What is the best way to merge two arrays (element + element), if elements itself are arrays

I have two nested arrays with equal size:
Array1 =[[1, 2], [], [2, 3]]
Array2= [[1, 4], [8, 11], [3, 6]]
I need to merge them in one array, like this:
Array = [[1,2,1,4], [8,11], [2,3,3,6]],
so each elements of new Array[x] = Array1[x] + Array2[x]
I understand how to do it with for(each) cycle, but I am sure Ruby has an elegant solution for that. It is also possible that the solution will produce by changing Array1.
Array1.each_index.map { |i| Array1[i] + Array2[i] }
#=> [[1,2,1,4], [8,11], [2,3,3,6]]
This has the advantage that it avoids the creation of a temporary array [Array1, Array2].transpose or Array1.zip(Array2).
[Array1, Array2].transpose.map(&:flatten)
=> [[1, 2, 1, 4], [8, 11], [2, 3, 3, 6]]
RubyGuides: "Turn Rows Into Columns With The Ruby Transpose Method"
Each step explained:
[Array1, Array2]
=> [[[1, 2], [], [2, 3]],
[[1, 4], [8, 11], [3, 6]]]
Create a grid like array.
[Array1, Array2].transpose
=> [[[1, 2], [1, 4]], [[], [8, 11]], [[2, 3], [3, 6]]]
transpose switches rows and columns (close to what we want)
[Array1, Array2].transpose.map(&:flatten)
=> [[1, 2, 1, 4], [8, 11], [2, 3, 3, 6]]
flatten gets rid of the unnecessary nested arrays (here combined with map to access nested arrays)
I would do something like:
array1 =[[1, 2], [], [2, 3]]
array2= [[1, 4], [8, 11], [3, 6]]
array1.zip(array2).map(&:flatten)
# => [[1, 2, 1, 4], [8, 11], [2, 3, 3, 6]]

Condensing multidimensional array of permutations into groups

I have a multidimensional array in Ruby which looks like this:
[[1,12], [1,5], [1,6], [5,12], [6,12], [12,5], [12,6]]
I need to combine it into a new multidimensional array, grouping values together, to form valid combinations of (n) positions (n being the size of each element in the original array):
[
[[1,5,6], [12]], # [1,5,6].product([12]) #=> [[1, 12], [5, 12], [6, 12]]
[[1,12], [5,6]] # [1,12].product([5,6]) #=> [[1, 5], [1, 6], [12, 5], [12, 6]]
]
The purpose of this is to take an array of n-place permutations, and generate the smallest possible multidimensional array that lists the valid numbers in each place (while not including combinations that aren't present in the original array).
How can a multidimensional array containing n-place permutations be reduced down to the array described above? The built-in (and brilliant) array methods in Ruby don't seem to include a function for this, and short of generating every possible combination and then testing them against the original permutations, I'm not sure how to get it right.
I'm not quite sure if I correctly understand the problem, but given this array:
a = [[1, 12], [1, 5], [1, 6], [5, 12], [6, 12], [12, 5], [12, 6]]
I think you can group the left-hand items by the right-hand items:
h1 = Hash.new { |h, k| h[k] = [] }
a.each { |k, v| h1[v] << k }
h1
#=> {12=>[1, 5, 6], 5=>[1, 12], 6=>[1, 12]}
And apply the same transformation again:
h2 = Hash.new { |h, k| h[k] = [] }
h1.each { |k, v| h2[v] << k }
h2
#=> {[1, 5, 6]=>[12], [1, 12]=>[5, 6]}
This gives:
h2.to_a
#=> [
# [[1, 5, 6], [12]],
# [[1, 12], [5, 6]]
# ]
In recent versions of Ruby you could write it as:
a.group_by(&:last).transform_values { |v| v.map(&:first) }
.group_by(&:last).transform_values { |v| v.map(&:first) }
.to_a
#=> [
# [[1, 5, 6], [12]],
# [[1, 12], [5, 6]]
# ]
The condensed array can be expanded via:
[[[1, 5, 6], [12]], [[1, 12], [5, 6]]].flat_map { |a, b| a.product(b) }
#=> [[1, 12], [5, 12], [6, 12], [1, 5], [1, 6], [12, 5], [12, 6]]
Note that this attempt only works for sub-arrays with two elements, but it should get you started.

How to count number or rectangles in a 2D array with Ruby?

Suppose I have an array. I wish to identify a rectangles top-left and bottom-right point. This is easy with a single rectangle. See my implementation below.
The problem becomes challenging when there are multiple rectangles. How do you identify top-left and bottom-right points with multiple rectangles?
rules continuous 0's are rectangles: In the below example there are 3 rectangles.
array = [
[1,1,1,1,0,0],
[1,0,1,1,0,0],
[1,0,1,1,0,0],
[1,1,0,0,1,1],
[1,1,0,0,1,1]]
def parse_array(arr)
answer = []
arr.each_with_index do |sub_array, x|
sub_array.each_with_index do |number, y|
if number == 0
answer.push([x,y])
end
end
end
answer
end
def edges(arr)
[arr.first, arr.last]
end
def get_length_and_width(arr)
width = (arr[1][1] - arr[0][1]) + 1
height = (arr[1][0] - arr[0][0]) + 1
[width, height]
end
Basic idea:
Deep-clone the array so we don't mess it up
Find a top-left corner of a rectangle
See how high it goes
See how wide it goes
Fill it up so we don't find those spaces any more
Repeat till no zeroes remain
So...
class RectFinder < Array
def initialize(array)
super()
#a = array.map { |x| x.dup }
#h = array.size
#w = array.first.size
find_rects
end
private def find_rects
(0...#h).each do |r|
(0...#w).each do |c|
if #a[r][c] == 0
self << find_rect(r, c)
end
end
end
end
private def find_rect(r, c)
w = ((c + 1)...#w).take_while { |cc| #a[r][cc] == 0 }.size + 1
h = ((r + 1)...#h).take_while { |rr| (c...(c + w)).all? { |cc| #a[rr][cc] == 0 } }.size + 1
(r...(r + h)).each { |rr| #a[rr][c...(c + w)] = [1] * w }
[[r, c], [r + h - 1, c + w - 1]]
end
end
p RectFinder.new(array)
# => [[[0, 4], [2, 5]], [[1, 1], [2, 1]], [[3, 2], [4, 3]]]
My understanding is that the question is, when a given array with n elements ("rows"), each element being an array of m elements ("columns") is viewed as a matrix, construct an array of all submatrices that contain only zeroes, where each submatrix is identified by the coordinates of its upper-left and lower-right elements. For example, the elements array[1][4], array[1][5], array[2][4] and array[2][5], which all equal zero, comprise a submatrix identified by the pair of coordinates [[1,4],[2,5]].
require 'matrix'
def all_zero_subarrays(arr)
m = Matrix[*arr]
last_row, last_col = arr.size-1, arr.first.size-1
(0..last_row).each_with_object([]) do |i,a|
(0..last_col).each do |j|
next unless arr[i][j].zero?
(i..last_row).each do |ii|
(j..last_col).each do |jj|
next unless arr[ii][jj].zero?
a << [[i,j], [ii,jj]] if m.minor((i..ii), (j..jj)).to_a.flatten.uniq == [0]
end
end
end
end
end
array = [
[1,1,1,1,0,0],
[1,0,1,1,0,0],
[1,0,1,1,0,0],
[1,1,0,0,1,1],
[1,1,0,1,1,1]]
I changed array[4][3] from 0 to 1, array having been defined in the question. My reason for doing so will be made clear later.
arr = all_zero_subarrays(array)
#=> [[[0, 4], [0, 4]], [[0, 4], [0, 5]], [[0, 4], [1, 4]], [[0, 4], [1, 5]],
# [[0, 4], [2, 4]], [[0, 4], [2, 5]], [[0, 5], [0, 5]], [[0, 5], [1, 5]],
# [[0, 5], [2, 5]],
# [[1, 1], [1, 1]], [[1, 1], [2, 1]], [[1, 4], [1, 4]], [[1, 4], [1, 5]],
# [[1, 4], [2, 4]], [[1, 4], [2, 5]], [[1, 5], [1, 5]], [[1, 5], [2, 5]],
# [[2, 1], [2, 1]], [[2, 4], [2, 4]], [[2, 4], [2, 5]], [[2, 5], [2, 5]],
# [[3, 2], [3, 2]], [[3, 2], [3, 3]], [[3, 2], [4, 2]], [[3, 3], [3, 3]],
# [[4, 2], [4, 2]]]
See Matrix::[] and Matrix#minor.
If desired, elements of arr that represent submatrices that are "contained" in another submatrix can be removed. For example, [[0, 4], [1, 5]] is "contained in [[0, 4], [2, 5]]. Here is a way of doing that.
def second_rect_in_first?(rect1, rect2)
ul1, br1 = rect1
ul2, br2 = rect2
first_ul_of_second?(ul1, ul2) && first_ul_of_second?(br2, br1)
end
def first_ul_of_second?((i1, j1), (i2, j2))
(i1 <= i2) && (j1 <= j2)
end
arr.each_with_object([]) do |rect, a|
next if a.any? { |arect| second_rect_in_first?(arect, rect) }
a.reject! { |arect| second_rect_in_first?(rect, arect) }
a << rect
end
#=> [[[0, 4], [2, 5]],
# [[1, 1], [2, 1]],
# [[3, 2], [3, 3]],
# [[3, 2], [4, 2]]]
Note that [[3, 2], [3, 3]] and [[3, 2], [4, 2]]] both include [3, 2].

Finding index and size of consecutive repeated elements in an array

An array consists of 1, 2, and 0s. I am trying to identify the maximum repetition and its starting index within the array.
Example:
2 2 1 0 2 2 2 0 1 1
The method should accept an integer arguement, which can be one of the numbers 1 or 2
If we demonstrate these inputs on above array, the outputs would be:
find_duplicates(2)
=> 3,4
find_duplicates(1)
=> 2,8
where the first number indicates the size of the duplication, and second is the starting index of it.
I tried looping through the array and compare with arr[i+1] or arr[-1], but this is not the correct approach. Any help will be greatly appreciated.
Edit:
I had not pasted what I had tried at the time I asked the question, this is not something I would do if I could feel some confidence on the way I followed:
def find_status(arr,participant)
status = Array.new
#arr is a two dimensional array
for i in 0...arr.length do
current_line=arr[i]
cons=0
for j in 0...current_line.length do
#I worked on lots of if/else/case statements here, this is just one of them
if current_line[j] == participant
cons+=1 #count consecutive
if current_line[j]!=participant
cons=0
end
end
status[i] = cons
end
end
return status
end
def max_run(arr, target)
_,b = arr.each_with_index.
chunk { |n,_| n==target }.
select { |tf,_| tf==true }.
max_by { |_,a| a.size }
b ? [b.size, b.first.last] : nil
end
arr = [1,1,2,2,2,3,1,1,1,1,2,2,2,2,3,3]
max_run(arr,1) #=> [4, 6]
max_run(arr,2) #=> [4, 10]
max_run(arr,3) #=> [2, 14]
max_run(arr,4) #=> nil
For target = 2, the steps are as follows:
enum0 = arr.each_with_index
#=> #<Enumerator: [1, 1, 2, 2, 2, 3, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3]
# :each_with_index>
We can see the elements that will be generated by this enumerator by converting it to an array:
enum0.to_a
#=> [[1, 0], [1, 1], [2, 2], [2, 3], [2, 4], [3, 5], [1, 6], [1, 7], [1, 8],
# [1, 9], [2, 10], [2, 11], [2, 12], [2, 13], [3, 14], [3, 15]]
Continuing,
enum1 = enum0.chunk { |n,_| n==target }
#=> #<Enumerator: #<Enumerator::Generator:0x007f9beb9b0850>:each>
Carefully examine the return value here. You can think of enum1 as a "compound enumerator". It will generate the following values:
enum1.to_a
#=> [[false, [[1, 0], [1, 1]]], [true, [[2, 2], [2, 3], [2, 4]]],
# [false, [[3, 5], [1, 6], [1, 7], [1, 8], [1, 9]]],
# [true, [[2, 10], [2, 11], [2, 12], [2, 13]]], [false, [[3, 14], [3, 15]]]]
Continuing,
c = enum1.select { |tf,_| tf==true }
#=> [[true, [[2, 2], [2, 3], [2, 4]]],
# [true, [[2, 10], [2, 11], [2, 12], [2, 13]]]]
_,b = c.max_by { |_,a| a.size }
#=> [true, [[2, 10], [2, 11], [2, 12], [2, 13]]]
b #=> [[2, 10], [2, 11], [2, 12], [2, 13]]
b ? [b.size, b.first.last] : nil
#=> [[2, 10], [2, 11], [2, 12], [2, 13]] ? [4, [2,10].last]
#=> [4, 10]
a = [2, 2, 1, 0, 2, 2, 2, 0, 1, 1]
longest_sequence =
a.each_index.select{|i| a[i] == 2}.chunk_while{|i, j| i.next == j}.max_by(&:length)
# => [4, 5, 6]
[longest_sequence.length, longest_sequence.first] # => [3, 4]
The solution below is likely most efficient since it is O(N). It walks through an array, collecting the chunks:
arr.each.with_index.reduce({idx:-1, i: -1, len: 0}) do |memo, (e, i)|
memo[:i] = i if memo[:i] == -1 && e == 2 # at the beginning of chunk
memo[:len], memo[:idx] = [i - memo[:i], memo[:i]] \
if memo[:i] >= 0 && i - memo[:i] > memo[:len] # save values if needed
memo[:i] = -1 unless e == 2 # reset index counter
memo
end.reject { |k, _| k == :i } # reject temporary index value
#⇒ {
# :idx => 4,
# :len => 3
# }
To use it as method, accepting a parameter; just wrap the code above with def find_duplicates number and substitute 2 with number in the code above. Yes, it returns hash instead of an array.

Joining two ranges into 2d array Ruby

How do I join two ranges into a 2d array as such in ruby? Using zip doesn't provide the result I need.
(0..2) and (0..2)
# should become => [[0,0],[0,1],[0,2], [1,0],[1,1],[1,2], [2,0],[2,1],[2,2]]
Ruby has a built in method for this: repeated_permutation.
(0..2).to_a.repeated_permutation(2).to_a
I'm puzzled. Here it is a day after the question was posted and nobody has suggested the obvious: Array#product:
[*0..2].product [*1..3]
#=> [[0, 1], [0, 2], [0, 3], [1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3]]
range_a = (0..2)
range_b = (5..8)
def custom_join(a, b)
a.inject([]){|carry, a_val| carry += b.collect{|b_val| [a_val, b_val]}}
end
p custom_join(range_a, range_b)
Output:
[[0, 5], [0, 6], [0, 7], [0, 8], [1, 5], [1, 6], [1, 7], [1, 8], [2, 5], [2, 6], [2, 7], [2, 8]]
straight forward solution:
range_a = (0..2)
range_b = (5..8)
def custom_join(a, b)
[].tap{|result| a.map{|i| b.map{|j| result << [i, j]; } } }
end
p custom_join(range_a, range_b)
Output:
[[0, 5], [0, 6], [0, 7], [0, 8], [1, 5], [1, 6], [1, 7], [1, 8], [2, 5], [2, 6], [2, 7], [2, 8]]
Simply, this will do it:
a = (0...2).to_a
b = (0..2).to_a
result = []
a.each { |ae| b.each { |be| result << [ae, be] } }
p result
# => [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]

Resources