iterate with a method within ruby class - ruby

Class Image initializes with an array of 0's and 1's. I have method transform, such that
[[0,0,0],
[0,1,0],
[0,0,0]]
returns
[[0,1,0],
[1,1,1],
[0,1,0]]
I want to implement method blur(n), which iterates n times with transform, such calling blur(2) with
[[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0]]
returns
[[0,0,0,0,1,0,0,0,0],
[0,0,0,1,1,1,0,0,0],
[0,0,1,1,1,1,1,0,0],
[0,0,0,1,1,1,0,0,0],
[0,0,0,0,1,0,0,0,0]]
I'm trying to use transform iteratively to achieve this, but I'm getting undefined method 'map' for #<Context::Image:0x000000012eb020> when calling blur with an instance of Image. How can I iterate over each successive transformation, such that blur returns the latest version with the maximum n transformations?
class Image
attr_accessor :array
def initialize(array)
self.array = array
end
def output_image
self.array.each do |item|
puts item.join
end
end
def transform #changes adjacent a 1's adjacent 0's into 1
cloned = self.array.map(&:clone)
#scan original array for 1; map crosses into clone if found
self.array.each.with_index do |row, row_index|
row.each.with_index do |cell, col|
if cell == 1
cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
cloned[row_index][col-1] = 1 unless col.zero? # copy left
cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
end
end
end
cloned
end
def blur(n) #should call transform iteratively n times
blurred = Image.new(self)
n.times do
blurred = blurred.transform
end
blurred
end
end

You could use the Matrix class.
require 'matrix'
class Matrix
def el(r,c)
if r < 0 || r >= row_count || c < 0 || c >= column_count
0
else
self[r,c]
end
end
def transform
Matrix.build(row_count, column_count) { |r,c|
[el(r,c), el(r-1,c), el(r+1,c), el(r,c-1), el(r,c+1)].max }
end
end
Given a row-column pair, r, c, the helper method el returns 0 if the row or column is outside the bounds of the matrix and the value at [r,c] otherwise.
nrows = 5
ncols = 5
m = Matrix.build(nrows, ncols) { |r,c| (r==nrows/2 && c==ncols/2) ? 1 : 0 }
#=> Matrix[[0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]]
m = m.transform
#=> Matrix[[0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0],
# [0, 0, 0, 0, 0]]
m = m.transform
# Matrix[[0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [1, 1, 1, 1, 1],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0]]
m.to_a
#=> [[0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [1, 1, 1, 1, 1],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0]]

map is a method available to an Array, but not to your custom class Image.
I suggest calling map on your instance variable #array instead. Then, when your transforms are completed, create a new Image instance with that transformed array.
Below is an example of code that should work. Note that transform and blur take input arrays as parameters, so they do not rely on any instance state. Therefore, I've made them class methods instead of instance methods. This allows your users to use them without having to create an instance, if all they want to do is the array transformation. It also makes those methods easy to extract to a module in future refactorings. I've added an instance method, blurred_image, which applies the transformation to the instance and returns a new Image instance.
def self.transform(input_array) #changes adjacent a 1's adjacent 0's into 1
cloned = input_array.map(&:clone)
#scan original array for 1; map crosses into clone if found
input_array.each.with_index do |row, row_index|
row.each.with_index do |cell, col|
if cell == 1
cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
cloned[row_index][col-1] = 1 unless col.zero? # copy left
cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
end
end
end
cloned
end
def self.blur(input_array, transform_count) #should call transform iteratively n times
blurred = input_array
transform_count.times { blurred = transform(blurred) }
Image.new(blurred)
end
def blurred_image(transform_count)
self.class.new(self.class.blur(array, transform_count))
end

Related

convert adjacency matrix to adjacency list to represent a graph

I am trying to write a code in Ruby to convert a given graph's adjacency matrix to adjacency list. When I verified my solution for the given input mentioned here my output was slightly different. I need some direction in understanding where exactly is the issue. Below is my code:
def convert_adj_matrix_to_list(arr)
list = {}
start = 0
last = 0
arr.each_index do |row|
# row = [0, 0, 1]
puts "row = #{arr[row]}"
for col in 0..arr[row].size-1
puts "row = #{row}, col = #{col}"
puts "arr[#{row}][#{col}] = #{arr[row][col]}"
list[row] = col if arr[row][col] == 1
end
puts
end
list
end
arr = [ [0, 0, 1], [0, 0, 1], [1, 1, 0] ]
puts convert_adj_matrix_to_list(arr)
Output:
row = [0, 0, 1]
row = 0, col = 0
arr[0][0] = 0
row = 0, col = 1
arr[0][1] = 0
row = 0, col = 2
arr[0][2] = 1
row = [0, 0, 1]
row = 1, col = 0
arr[1][0] = 0
row = 1, col = 1
arr[1][1] = 0
row = 1, col = 2
arr[1][2] = 1
row = [1, 1, 0]
row = 2, col = 0
arr[2][0] = 1
row = 2, col = 1
arr[2][1] = 1
row = 2, col = 2
arr[2][2] = 0
{0=>2, 1=>2, 2=>1}
Printing inside a function like this is a side effect and should only be done for debugging. Better to print in the caller.
Logically, your code is only keeping track of the last link seen in the matrix and returns a hash that maps ints to ints rather than a hash that maps ints to int arrays.
You can get the hash by mapping each row, then filtering the row's indexes based on whether the cell has the value 1. Once you've built a 2d array of pairs with the first item in a pair being the source node integer and the second item in a pair being the array of its edges to other nodes, call .to_h on it to produce a hash.
def adj_matrix_to_list(matrix)
matrix.each_with_index.map do |row, i|
[i, row.each_index.select {|j| row[j] == 1}]
end.to_h
end
matrix = [[0, 0, 1],
[0, 0, 1],
[1, 1, 0]]
p adj_matrix_to_list(matrix) # {0=>[2], 1=>[2], 2=>[0, 1]}
Having said that, using a hash with keys 0, 1, 2 ... n is an antipattern, since arrays are a faster and more natural way to represent a sequentially-indexed list of things. I'd do:
def adj_matrix_to_list(matrix)
matrix.map do |row|
row.each_index.select {|i| row[i] == 1}
end
end
matrix = [[0, 0, 1],
[0, 0, 1],
[1, 1, 0]]
p adj_matrix_to_list(matrix) # [[2], [2], [0, 1]]
and usage is the same as the hash version.

Find count of sub arrays of zeros in an array

I need to find the count of sub arrays of zeros in an array:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
Result should be: 3 because we have 0, 0, 0 and 0, 0, 0.
Counting the number of zeros (6) will not work.
array.join.squeeze('0').count('0')
#=> 3
We have
s = array.join
#=> "11100111101110001"
t = s.squeeze('0')
#=> "11101111011101"
t.count('0')
#=> 3
Note one could squeeze all the characters, not just the zeroes (squeeze as opposed to squeeze('0')).
Another way:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0]
array.select.with_index(1) { |n,i| n.zero? && array[i] != 0 }.size
#=> 3
When, as here, the last element of array is a zero, array[i] #=> nil when i = array.size (since i goes from 1 to array.size).
chunk_while and count might work:
array
.chunk_while(&:==) # [[1, 1, 1], [0, 0], [1, 1, 1, 1], [0], [1, 1, 1], [0, 0, 0], [1]]
.count { |arr| arr.include?(0) } # 3
Or join, scan and length:
arr
.join # "11100111101110001"
.scan(/(0+)/) # [["00"], ["0"], ["000"]]
.length # 3
You can use Enumerable#chunk and Enumerable#count to handle this as well like so:
Option 1:
arr = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
arr.chunk(&:zero?).count(&:first)
#=> 3
chunk will group consecutive elements together by their return value in the form of [return_value, [*elements]] so in this case chunk(&:zero?) will create groups like so
arr.chunk(&:zero?).to_a
#=> [[false, [1, 1, 1]],
# [true, [0, 0]],
# [false, [1, 1, 1, 1]],
# [true, [0]],
# [false, [1, 1, 1]],
# [true, [0, 0, 0]],
# [false, [1]]]
However chunk does not create a new Array (thus the to_a above to show the structure) instead it creates an Enumerator that stores this block of code and evaluates the return value upon subsequent method calls.
In this case we are then calling count(&:first) since count will only count the elements for which the block returns a truthy value (not nil or false).
Option 2:
arr.chunk {|e| e.zero? || nil}.count
#=> 3
Very similar to Option 1 this works because chunk will drop all elements where the return value is nil meaning
arr.chunk {|e| e.zero? || nil}.to_a
#=> [[true, [0, 0]], [true, [0]], [true, [0, 0, 0]]]
Bonus: (just for fun in case you need to count other consecutive elements)
groups = arr.chunk(&:itself)
.each_with_object(Hash.new {|h,k| h[k] =[]}) do |(e,arr),obj|
obj[e] << arr
end
#=> {1=>[[1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1]],
# 0=>[[0, 0], [0], [0, 0, 0]]}
groups[0].size
#=> 3
Yet another way...
array.each_with_object([]){ |a, ary| ary << a unless ary.last == a }.count(&:zero?)
I am providing this solution to understand the basic solution's idea of this problem.
This is c++ solution for counting number of 0 sub-array. Increment result when we get a 0. And when we get more than one zero then don't count for them.
int n = arr.size();
int cnt = 0;
for(int i = 0; i < n; i++) {
if(i > 0 && arr[i] == arr[i-1]) {
continue;
}
if(arr[i] == 0) {
cnt++;
}
}
cout << cnt << endl;

Why does my diagonal matrix calculator not return the total?

My method should take an array of subarrays, find the sum of the first value of the first array, the second value of the second array, the third value of the third array, and so on. Some examples of inputs and expected results are as follows:
exampleArray = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 4
exampleArray = [
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 5
I wrote this:
def diagonalSum(matrix)
total = 0
counter = 0
while matrix.length <= counter + 1 do
total += matrix[counter][counter]
counter += 1
end
total
end
and it returns 0.
It's easiest to convert the array to a matrix and apply Matrix#trace.
require 'matrix'
arr = [[1, 0, 0, 7],
[0, 2, 0, 0],
[0, 0, 3, 0],
[8, 0, 0, 4]]
Matrix[*arr].trace
#=> 10
According to the code you provide, in which the input is an array of arrays, the first advice I could give you is that in Ruby you must avoid using for/while loops and make use of iterators such as each/each_with_index instead (based on this Ruby style guide and the suggestions of #tadman and #Yu Hao).
The each with index iterator takes a Ruby block with the current array of the iteration along with its index position, so you don't need to define your own index variable and update it in every iteration.
Applying this to your code will result in the following:
def diagonal_sum(matrix)
total = 0
matrix.each_with_index do |row, index|
total+=row[index]
end
total
end
Also note that the convention in Ruby is to write variable and method names in snake_case (according to the previous style guide).

Is there a ruby idiom for popping items from an array while a condition is true

Is there a Ruby idiom for popping items from an array while a condition is true, and returning the collection?
I.e,
# Would pop all negative numbers from the end of 'array' and place them into 'result'.
result = array.pop {|i| i < 0}
From what I can tell, something like the above doesn't exist.
I'm currently using
result = []
while array.last < 0 do
result << array.pop
end
Maybe you are looking for take_while?
array = [-1, -2, 0, 34, 42, -8, -4]
result = array.reverse.take_while { |x| x < 0 }
result would be [-8, -4].
To get the original result back you could use drop_while instead.
result = array.reverse.drop_while { |x| x < 0 }.reverse
result would be [-1, -2, 0, 34, 42] in this case.
You could write it yourself:
class Array
def pop_while(&block)
result = []
while not self.empty? and yield(self.last)
result << self.pop
end
return result
end
end
result = array.pop_while { |i| i < 0 }
In case your looking for a solution to pop all items that satisfy a condition, consider a select followed by a delete_if, e.g.
x = [*-10..10].sample(10)
# [-9, -2, -8, 0, 7, 9, -1, 10, -10, 3]
neg = x.select {|i| i < 0}
# [-9, -2, -8, -1, -10]
pos = x.delete_if {|i| i < 0}
# [0, 7, 9, 10, 3]
# note that `delete_if` modifies x
# so at this point `pos == x`

Build efficient array integer incrementer with different caps per number

I want to program a counter which is represented by an array of numbers, starting with:
[0, 0, 0]
The constraint here is, that each position has a different cap, so it's not necessarily 9 or something else, but it is given. For instance:
[4, 2, 1]
Which would lead to the following incrementation sequence:
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[0, 2, 0]
[0, 2, 1]
[1, 0, 0]
.
.
.
Of course I can think of a solution using modulo and adding each carryover onto the next position. But has someone an idea how to implement this efficiently, respectively with nice Ruby syntax without cluttering it too much?
That is my naive implementation:
max = [10, 1, 1, 1, 10]
counter = [0, 0, 0, 0, 0]
i = counter.length-1
while counter != max do
counter[i] = counter[i] + 1
while counter[i] > max[i]
counter[i] = 0
i = i - 1
counter[i] = counter[i] + 1
end
i = counter.length-1
end
I'm not sure about efficiency but here's my shot at it:
start = [0, 0, 0]
cap = [4, 2, 1]
start.zip(cap).map{ |i, c| (i..c).to_a }.reduce(&:product).map &:flatten
Produces something like:
[[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[0, 2, 0],
[0, 2, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1],
[1, 2, 0],
[1, 2, 1],
[2, 0, 0],
[2, 0, 1]...]
Edit: I was writing this before you made your edit. It seemed like you wanted a counter object, not just to output a list.
1) I would recommend specifying not the limits but (limit+1) of each of the digits. For example, for a [second, minute, hour, day, year] counter it makes more sense (to me) to write [60, 60, 24, 365] instead of [59,59,23,364].
2) You'll have to figure out what to do if your counter overflows the last limit of your array. I added an extra position that counts to infinity.
3) I would also recommend reversing the order of the array, at least in the internal representation to avoid inverting subscripts. If you don't want it like that, you can .reverse the bases in initialize and #digits in to_s
class MyCounter
def initialize bases
#bases = bases
#bases << 1.0/0 # Infinity
#digits = Array.new(bases.size, 0)
prod = 1
#digit_values = [1] + #bases[0..-2].map { |b| prod *= b }
end
attr_reader :digit_values
def to_s
#digits
end
def increment(digit=0)
v = #digits[digit] + 1
if v < #bases[digit]
#digits[digit] = v
else
#digits[digit] = 0
increment(digit+1)
end
self
end
def +(integer)
(#digits.size - 1).step(0,-1).each do |i|
#digits[i] += integer / #digit_values[i]
integer = integer % #digit_values[i]
end
self
end
end
c1 = MyCounter.new [2,3,5]
20.times { c1.increment; p c1 }
c2 = MyCounter.new [2,3,5]
c2 += 20
p c2
Create an array for each cap, with values from 0 upto cap. Take the first array and calculate the Cartesian product with the rest of the arrays.
caps = [4, 2, 1]
arrs = caps.map{|cap| (0..cap).to_a} #=>[[0, 1, 2, 3, 4], [0, 1, 2], [0, 1]]
p arrs.shift.product(*arrs)
# =>[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [0, 2, 0], [0, 2, 1], ...
If you don't want a memory-consuming array with the results, then provide a block. product will yield each element to it, one by one.
arrs = caps.map{|cap| (0..cap).to_a}
arrs.shift.product(*arrs){|el| puts el.join} #no resulting array
#000
#001
#010
#011
#...

Resources