Why is my enumeration stopping after first rejection in Ruby? - arrays

Desperate need of help. I am trying to remove arrays from and array of arrays, and I have hit a road block. Essentially, if the first value in the child-array doesn't exist in either position of any other child-arrays, then it should be deleted. (presume that the array will be sorted - cause it will be)
arr = [[0, 1], [2, 3], [4, 5]]
arr.each_with_index do |inside_array, index|
if !index.zero?
# arr.delete(arr[index]) if arr.select {|x| x.include?(inside_array[0])}.count < 2
# refactored
arr.reject! {|x| x.include?(inside_array[0])}
end
end
=> [[0, 1], [4, 5]]
# Why does it stop itterating/enumerating after the first deletion?
# Goal output is [[0, 1]] for this example
Similarly, an array such as [[0, 1], [2, 3], [1, 5]], should yield [[0, 1], [1, 5]]
-or -
[[0, 1], [2, 3], [0, 3]], should yield [[0, 1], [0, 3]]

You've tried to modify origin array. That's your problem.
In that cases you need to duplicate it like this:
arr = [[0, 1], [2, 3], [4, 5]]
arr.dup.each_with_index do |inside_array, index|
if !index.zero?
arr.reject! {|x| x.include?(inside_array[0])}
end
end
arr #=> [[0, 1]]
So just use dup
As for the second question (implementation of subarray removal), I suggest this refactoring:
def remove_subarray(arr)
arr.reject { |inside_array| (inside_array & arr.first).empty? }
end
remove_subarray([[0, 1], [2, 3], [4, 5]]) #=> [[0, 1]]
remove_subarray([[0, 1], [2, 3], [1, 5]]) #=> [[0, 1], [1, 5]]
remove_subarray([[0, 1], [2, 3], [0, 3]]) #=> [[0, 1], [0, 3]]

Related

Transforming an array using Ruby

how would I transform this array :
[["10"], ["20"], ["30"], ["40"], ["21"], ["31"], ["41"], ["32"], ["42"], ["43"]]
into this array
[[1, 0], [2, 0], [3, 0], [4, 0], [2, 1], [3 1,], [4, 1], [3, 2], [4, 2], [4, 3]]
Ideally in the most beginner friendly version possible please.
I'm failing to iterate through the array thoroughly and use the correct methods in the correct order.
If the input is always as in your example (an array of arrays, where every array has a single element which is a string) then you can map to get the chars and then map that result to get them as integers;
[["10"], ["20"], ["30"], ["40"], ["21"], ["31"], ["41"], ["32"], ["42"], ["43"]]
.map { |str, _| str.chars.map(&:to_i) }
# [[1, 0], [2, 0], [3, 0], [4, 0], [2, 1], [3, 1], [4, 1], [3, 2], [4, 2], [4, 3]]

Optimised loop for array using ruby

I have (m = rows-1, n = cols-1) dimensional matrix.
And I passing i to method which will return array in following manner (provided with i <= m,n)
Suppose n=0, so for 4x4 matrix, it will return boundary elements position.
Do not consider below as ruby syntax, get only flow.
square = [[i,i] -> [i, m-i] -> [n-i, m-1] -> [n-i, i] -> [i,i]]
(no data is repeated in above)
I achieved above in recursion manner by setting parameters but I need easier/optimised trick.
Update - for user sawa
arr = [*1..16].each_slice(4).to_a
m,n = arr.length-1, arr[0].length-1
loop_count = 0
output = [[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [4, 1], [4, 2], [3, 2], [2, 2], [1, 2], [0, 2], [0, 1]]
loop_count = 1
output = [[1, 1], [2, 1], [2, 2], [1, 2]]
I ended up with this solution, but I think there is a better way out there.
First define a method to print the matrix mapped by indexes, just to check if the result id correct:
def print_matrix(n,m)
range_n, range_m = (0..n-1), (0..m-1)
mapp = range_m.map { |y| range_n.map { |x| [x, y] } }
mapp.each { |e| p e }
puts "-" * 8 * n
end
Then define a method that returns the frame starting from the loop s (where 0 is the external frame):
def frame (n, m, s = 0)
res = []
return res if (s >= n/2 and s >= m/2) and (n.even? or m.even?)
(s..n-s-1).each { |x| res << [x,s] }
(s..m-s-1).each { |y| res << [res.last[0], y] }
(res.last[0].downto s).each { |x| res << [x, res.last[1]] }
(res.last[1].downto s).each { |y| res << [res.last[0], y] }
res.uniq
end
Now, call the methods and check the output:
n, m, loops = 4, 4, 1
print_matrix(n,m)
frame(n, m, loops)
# [[0, 0], [1, 0], [2, 0], [3, 0]]
# [[0, 1], [1, 1], [2, 1], [3, 1]]
# [[0, 2], [1, 2], [2, 2], [3, 2]]
# [[0, 3], [1, 3], [2, 3], [3, 3]]
# --------------------------------
# [[1, 1], [2, 1], [2, 2], [1, 2]]
Here we can use Matrix methods to advantage, specifically Matrix::build, Matrix#minor and Matrix#[].
Code
require 'matrix'
def border_indices(nrows, ncols, i)
m = Matrix.build(nrows, ncols) { |r,c| [r,c] }.minor(i..nrows-1-i, i..ncols-1-i)
[[1,0,m.row_count-1], [0,1,m.column_count-1],
[-1,0,m.row_count-1], [0,-1,m.column_count-2]].
each_with_object([[0,0]]) do |(x,y,n),a|
n.times { a << [a.last.first+x, a.last.last+y] }
end.map { |i,j| m[i,j] }
end
Examples
nrows = 5
ncols = 6
border_indices(nrows, ncols, 0)
#=> [[0, 0], [1, 0], [2, 0], [3, 0],
# [4, 0], [4, 1], [4, 2], [4, 3], [4, 4],
# [4, 5], [3, 5], [2, 5], [1, 5],
# [0, 5], [0, 4], [0, 3], [0, 2], [0, 1]]
border_indices(nrows, ncols, 1)
#=> [[1, 1], [2, 1],
# [3, 1], [3, 2], [3, 3],
# [3, 4], [2, 4],
# [1, 4], [1, 3], [1, 2]]
border_indices(nrows, ncols, 2)
#=> [[2, 2], [2, 3]]
Explanation
Consider the calculation of border_indices(5, 6, 1).
nrows = 5
ncols = 6
i = 1
mat = Matrix.build(nrows, ncols) { |r,c| [r,c] }
#=> Matrix[[[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5]],
# [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5]],
# [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5]],
# [[3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5]],
# [[4, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5]]]
m = mat.minor(i..nrows-1-i, i..ncols-1-i)
#=> mat.minor(1..3, 1..4)
#=> Matrix[[[1, 1], [1, 2], [1, 3], [1, 4]],
# [[2, 1], [2, 2], [2, 3], [2, 4]],
# [[3, 1], [3, 2], [3, 3], [3, 4]]]
b = [[1,0,m.row_count-1], [0,1,m.column_count-1],
[-1,0,m.row_count-1], [0,-1,m.column_count-2]]
#=> [[1, 0, 2], [0, 1, 3], [-1, 0, 2], [0, -1, 2]]
c = b.each_with_object([[0,0]]) do |(x,y,n),a|
n.times { a << [a.last.first+x, a.last.last+y] }
end
#=> [[0, 0], [1, 0],
# [2, 0], [2, 1], [2, 2],
# [2, 3], [1, 3],
# [0, 3], [0, 2], [0, 1]]
c.map { |i,j| m[i,j] }
#=> [[1, 1], [2, 1],
# [3, 1], [3, 2], [3, 3],
# [3, 4], [2, 4],
# [1, 4], [1, 3], [1, 2]]
Note that in the calculation of c, a.last is the last pair of indices added to the array being constructed (a.last = [a.last.first, a.last.last]).
Following will work for both m == n & m != n case.
I hope, all will consider what matrix variable below stands for (2 D array)
def matrixRotation(matrix)
m,n = matrix.length-1, matrix[0].length-1
loop_count = [m,n].min/2
0.upto(loop_count) do |i|
indices = []
i.upto(m-i) { |j| indices << [j, i] }
i.upto(n-i) { |j| indices << [m-i, j] }
i.upto(m-i) { |j| indices << [m-j, n-i] }
i.upto(n-i) { |j| indices << [i, n-j] }
puts "-------------- For Layer #{i+1} ---------------", nil
indices = indices.uniq
values = indices.map { |x| matrix[x[0]][x[1]] }
puts 'indices:', indices.inspect, nil, 'values:', values.inspect
end
end

How to zip every element of array to every element of another array in ruby?

Say I have this array:
[0, 1, 4], [2, 3]
How can I merge them to get:
[0,2], [0,3], [1,2], [1,3], [4,2], [4,3]
I tried:
[0, 1, 4].zip [2, 3]
But I got:
[[0, 2], [1, 3], [4, nil]]
Any ideas?
[0, 1, 4].product([2, 3])
That should generate:
[[0, 2], [0, 3], [1, 2], [1, 3], [4, 2], [4, 3]]

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].

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