Scenario : I want to count no. of subsets having negative numbers between two zeros. Here no. of subsets with negative numbers can be consider as travelling down the hill from sea level
Consider a array
a=[0,1,0,-1,-2,-l,0]
0 here represent sea level. Numbers between zeros are steps going up/down and is represent by positive/negative consecutive steps.
You can move any steps up/down but it should come to sea level before moving up/down
If you consider
0,1,0 then it means that you moved from sea level to uphill and then again to sea level
0, 1, 0
similarly
From sea level to moving down one step, second step down, one step up and then again to sea level
0, -1, -2, -1, 0
Now considering an example.
a=[0,1,0,-1,-2,-1,0]
subset #1 = [0,1]
subset #2 = [0,-1,-2,-1,0]
Output: 1 (One time travelling below sea level)
Consider another example
b = [0,1,2,1,0,-1,-2,-1,0,1,2,1,0,-1,0]
subset #1 = [0,1,2,1,0] (moving up)
subset #2 = [0,-1,-2,-1,0] (moving down)
subset #3 = [0,1,2,1,0] (moving up)
subset #4 = [0,-1,0] (moving down)
Output: 2 (Two times travelling below sea level)
I'm wondering if there's a reason that you couldn't just count the times that you have a zero followed by a negative number; something like:
def count_below(arr)
count = 0
arr.each_index do |i|
count += 1 if arr[i] == 0 && (arr[i + 1] || 0) < 0
end
count
end
irb(main):039:0> count_below([0,1,0,-1,-2,-1,0])
=> 1
irb(main):040:0> count_below([0,1,2,1,0,-1,-2,-1,0,1,2,1,0,-1,0])
=> 2
Option 1: Produce Desired "Output" (Number of times below "Sea Level")
Assumptions:
We start at or above Sea Level.
Proposed Solution:
b = [0,1,2,1,0,-1,-2,-1,0,1,2,1,0,-1,0]
b.each_cons(2).count {|a,b| !a.negative? && b.negative? }
Steps:
Create an Enumerator of consecutive 2 elements (b.each_cons(2))
Count each time the first element is not negative and the second is negative (count {|a,b| !a.negative? && b.negative? })
Option 2: To produce the slices in your question the following should work
Assumptions:
We always start at Sea Level (0)
One must touch Sea Level when ascending or descending (e.g. [0,1,-1] is considered invalid input)
The first example is incorrect and should be [[0,1,0],[0,-1,-2,-1,0]]
Proposed Solution:
b.each_cons(2).with_object([]) do |(a,b),obj|
obj << [a] if a.zero?
obj.last << b
end
#=> [[0, 1, 2, 1, 0], [0, -1, -2, -1, 0], [0, 1, 2, 1, 0], [0, -1, 0]]
Steps:
Create an Enumerator of consecutive 2 elements (b.each_cons(2))
Iterate with a Accumulator Array (with_object([]))
Every time the first element is 0 insert a new sub Array (obj << [a] if a.zero?)
Insert the second element into the current (last) sub Array (obj.last << b)
with_object will return the accumulator Array as the result
You could chain methods to get the same result as Option 1 (such as count {|a| a.any?(&:negative)})
My solution assumes that you wish to count the number of sub-arrays that begin and end with zero and from the initial zero monotonically decrease to a minimum (negative) value, then monotonically increase to the final zero.
def count_em(arr)
arr.flat_map.with_index { |e,i| (e.zero? && i > 0 && i < arr.size-1) ? [0,0] : [e] }
.slice_when { |*pair| pair == [0,0] }
.count { |a| downhill?(a) }
end
def downhill?(arr)
return false if arr[1] > 0
imin = arr.each_index.min_by { |i| arr[i] }
return false unless decreasing?(arr[0..imin])
decreasing?(arr[imin..-1].reverse)
end
def decreasing?(arr)
arr.each_cons(2).all? { |e,f| f < e }
end
count_em [0, 1, 0, -1, -2, -1, 0]
#=> 1
count_em [0, 1, 2, 1, 0, -1, -2, -1, 0, 1, 2, 1, 0, -1, 0]
#=> 2
count_em [0, -2, -1, -2, 0]
#=> 0
count_em [0, -1, -2, 1, 0]
#=> 0
The last two examples reflect my understanding of the question.
The calculations are as follows.
arr = [0, 1, 2, 1, 0, -1, -2, -1, 0, 1, 2, 1, 0, -1, 0]
Use Enumerable#flat_map and Enumerator#with_index to insert a zero into a copy of arr after every zero but the first and last.
a = arr.flat_map.with_index { |e,i| (e.zero? && i > 0 && i < arr.size-1) ? [0,0] : [e] }
#=> [0, 1, 2, 1, 0, 0, -1, -2, -1, 0, 0, 1, 2, 1, 0, 0, -1, 0]
We can now use Enumerable#slice_when to obtain each sub-array that begins and end with a zero.
enum = a.slice_when { |*pair| pair == [0,0] }
#=> #<Enumerator: #<Enumerator::Generator:0x00007fa9fd970f28>:each>
We can see the elements that will be generated by this enumerator by converting it to an array.
enum.to_a
#=> [[0, 1, 2, 1, 0], [0, -1, -2, -1, 0], [0, 1, 2, 1, 0], [0, -1, 0]]
Lastly, use Enumerable#count to count the number of the four arrays generated by enum that are "downhill".
enum.count { |a| downhill?(a) }
#=> 2
This is because
downhill? [0, 1, 2, 1, 0] #=> false
downhill? [0, -1, -2, -1, 0] #=> true
downhill? [0, 1, 2, 1, 0] #=> false
downhill? [0, -1, 0] #=> true
To see how downhill? works, let
arr = [0, -1, -2, -1, 0]
Then
arr[1] > 0
#=> -1 > 0 => false, so do not return
imin = arr.each_index.min_by { |i| arr[i] }
#=> 2
decreasing?(arr[0..imin])
#=> decreasing? [0, -1, -2] => false, so do not return
decreasing?(arr[imin..-1].reverse)
#=> decreasing? [-2, -1, 0].reverse => [0, -1, -2]
# decreasing? [0, -1, -2] => true
Let's now look at the calculations performed by decreasing?. Suppose
arr = [0, -1, -2]
We see that
enum = arr.each_cons(2)
#=> #<Enumerator: [0, -1, -2]:each_cons(2)>
enum generates the following arrays:
enum.to_a
#=> [[0, -1], [-1, -2]]
Hence
arr.each_cons(2).all? { |e,f| f < e }
#=> true
because -1 < 0 #=> true and -2 < -1 #=> true.
Related
Note: This question poses a problem that I have already solved, however I feel my solution is very rudimentary and that other people, like myself, would benefit from a discussion with input from more experienced developers. Different approaches to solving the problem, as well as more sophisticated methods and algorithms would be really appreciated. I feel this is a good place to learn how Ruby can tackle what I consider to be a fairly difficult problem for a beginner.
Given a 6x6 2D Array arr:
1 1 1 0 0 0
0 1 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
We define an hourglass in arr to be a subset of values with indices falling in this pattern in arr's graphical representation:
a b c
d
e f g
There are 16 hourglasses in arr and an hourglass sum is the sum of an hourglass' values. Calculate the hourglass sum for every hourglass in arr, then print the maximum hourglass sum.
For example, given the 2D array:
arr = [
[-9, -9, -9, 1, 1, 1],
[ 0, -9, 0, 4, 3, 2],
[-9, -9, -9, 1, 2, 3],
[ 0, 0, 8, 6, 6, 0],
[ 0, 0, 0, -2, 0, 0],
[ 0, 0, 1, 2, 4, 0]
]
We calculate the following hourglass values:
-63, -34, -9, 12,
-10, 0, 28, 23,
-27, -11, -2, 10,
9, 17, 25, 18
Our highest hourglass value is from the hourglass:
0 4 3
1
8 6 6
My solution is:
def hourglass_sum(arr)
hourglasses = []
arr.each_with_index do |row, i|
# rescue clause to prevent iterating outside the array
unless arr[i].nil?
arr[i].length.times do |iteration|
# generate n 3x3 arrays
r1 = arr[i][iteration...iteration+3]
r2 = arr[i+1][iteration...iteration+3] if arr[i+1] != nil
r3 = arr[i+2][iteration...iteration+3] if arr[i+2] != nil
# rescue clause to stop creating 3x3 arrays that fall outside given input array
if arr[i+1] != nil && arr[i+2] != nil
# take all values except indices 0 and 5 from the 9 element array
result = r1 + [r2[1]] + r3
hourglasses << result.sum unless result.include? nil
end
end
end
end
p hourglasses.max
end
arr = [[-9, -9, -9, 1, 1, 1], [0, -9, 0, 4, 3, 2], [-9, -9, -9, 1, 2, 3], [0, 0, 8, 6, 6, 0], [0, 0 ,0, -2, 0, 0], [0, 0, 1, 2, 4, 0]]
hourglass_sum(arr)
# => 28
One option is to use Matrix methods.
require 'matrix'
ma = Matrix[*arr]
#=> Matrix[[-9, -9, -9, 1, 1, 1],
# [ 0, -9, 0, 4, 3, 2],
# [-9, -9, -9, 1, 2, 3],
# [ 0, 0, 8, 6, 6, 0],
# [ 0, 0, 0, -2, 0, 0],
# [ 0, 0, 1, 2, 4, 0]]
mi = Matrix.build(6-3+1) { |i,j| [i,j] }
#=> Matrix[[[0, 0], [0, 1], [0, 2], [0, 3]],
# [[1, 0], [1, 1], [1, 2], [1, 3]],
# [[2, 0], [2, 1], [2, 2], [2, 3]],
# [[3, 0], [3, 1], [3, 2], [3, 3]]]
def hourglass_val(r,c,ma)
mm = ma.minor(r,3,c,3)
mm.sum - mm[1,0] - mm[1,2]
end
max_hg = mi.max_by { |r,c| hourglass_val(r,c,ma) }
#=> [1,2]
hourglass_val(*max_hg,ma)
#=> 28
[1,2] are the row and column indices of the top-left corner of an optimal hourglass in arr.
Here is an option I came up with.
def width_height(matrix)
[matrix.map(&:size).max || 0, matrix.size]
end
def sum_with_weight_matrix(number_matrix, weight_matrix)
number_width, number_height = width_height(number_matrix)
weight_width, weight_height = width_height(weight_matrix)
width_diff = number_width - weight_width
height_diff = number_height - weight_height
0.upto(height_diff).map do |y|
0.upto(width_diff).map do |x|
weight_height.times.sum do |ry|
weight_width.times.sum do |rx|
weight = weight_matrix.dig(ry, rx) || 0
number = number_matrix.dig(y + ry, x + rx) || 0
number * weight
end
end
end
end
end
arr = [
[-9, -9, -9, 1, 1, 1],
[ 0, -9, 0, 4, 3, 2],
[-9, -9, -9, 1, 2, 3],
[ 0, 0, 8, 6, 6, 0],
[ 0, 0, 0, -2, 0, 0],
[ 0, 0, 1, 2, 4, 0],
]
weights = [
[1, 1, 1],
[0, 1, 0],
[1, 1, 1],
]
sum_matrix = sum_with_weight_matrix(arr, weights)
#=> [
# [-63, -34, -9, 12],
# [-10, 0, 28, 23],
# [-27, -11, -2, 10],
# [ 9, 17, 25, 18]
# ]
max_sum = sum_matrix.flatten.max
#=> 28
This solution uses the width_diff and height_diff to create an output matrix (4x4 for the sample data 0.upto(6 - 3).to_a #=> [0, 1, 2, 3]). The indexes of the weight_matrix (rxand ry) will be used as relative index compared to the larger number_matrix.
If your 2d array always has the same number of elements for each sub-array you can replace matrix.map(&:size).max with matrix[0]&.size || 0 to speed up determining the matrix width. The current solution uses the maximum size of the sub-arrays. Sub-arrays having a smaller size will use 0 for the missing elements thus not effecting the sum.
My solution might be a bit variable heavy. I've done this to have descriptive variable names, that hopefully tell you most you need to know about the solution. You can shorten variable names, or remove them completely when you feel like you don't need them.
If something isn't clear just ask away in the comments.
Without using the Matrix class, here's how I've done it for any arbitrary rectangular array:
offsets = [[-1, -1], [-1, 0], [-1, 1], [0, 0], [1, -1], [1, 0], [1, 1]]
sums = 1.upto(arr.length - 2).flat_map do |i|
1.upto(arr[0].length - 2).map do |j|
offsets.map {|(x, y)| arr[i+x][j+y] }.sum
end
end
puts sums.max
The values we're interested in are just offsets from a current position. We can map out the values in the array relative to the current position by some row and column offset, sum them, then select the max of the sums.
I am wondering if there is a way to generate an array which, for example, would start from 0, increase by 1 until it reaches 3, and then decreases by 1 until it reaches 0 again, eg
[0,1,2,3,2,1,0]
and if I could specify the number of values in the array ahead of time, that would be great. For example, if I could set the lower bound(0), upper bound (3), increment(1), and length of array (9):
[].oscillate(0,3,1,9) would give me this:
[0,1,2,3,2,1,0,1,2]
As of now, the best thing I can come up with is this:
values = []
until values.count >= 9
values.pop
x=0
values << x && x+=1 while x < 3
values << x && x-=1 while x >= 0
end
Fun exercise!
You're looking for a triangle wave.
The formulas on Wikipedia are for the standard shape (between -1 and 1), but here's an adapted version for any wave position, period and amplitude :
def triangle_wave(min, max, increment, length, offset = 0)
amplitude = max - min
period = 2 * amplitude
Array.new(length) do |i|
min + ((increment * (i + offset) - amplitude) % period - amplitude).abs
end
end
puts triangle_wave(0, 3, 1, 9) == [0, 1, 2, 3, 2, 1, 0, 1, 2]
# true
p triangle_wave(-3, 3, 1, 20)
# => [-3, -2, -1, 0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -2, -1, 0, 1, 2, 3, 2]
p triangle_wave(5, 9, 2, 9)
# => [5, 7, 9, 7, 5, 7, 9, 7, 5]
p triangle_wave(0, 1, 0.25, 9)
# => [0.0, 0.25, 0.5, 0.75, 1.0, 0.75, 0.5, 0.25, 0.0]
p triangle_wave(-3, 0, 1, 9, 3)
# => [0, -1, -2, -3, -2, -1, 0, -1, -2]
p triangle_wave(0, 1, 1, 9)
# => [0, 1, 0, 1, 0, 1, 0, 1, 0]
min should be lower than max, increment should be positive and max-min should be divisible by increment. Those are restrictions on the input but not on the output : any wave can be generated.
This problem could be a textbook example of the use of Ruby's flip-flop operator.
As the question only makes sense when there is a non-negative integer steps such that high = low + steps * increment, I've replaced the method's argument high with steps.
def oscillate(low, steps, increment, length)
high = low + steps * increment
n = low
length.times.each_with_object([]) do |_,a|
a << n
n += (n==low)..(n==high-increment) ? increment : -increment
end
end
oscillate(0,3,1,9)
#=> [0, 1, 2, 3, 2, 1, 0, 1, 2]
oscillate(-1, 4, 2, 16)
#=> [-1, 1, 3, 5, 7, 5, 3, 1, -1, 1, 3, 5, 7, 5, 3, 1]
To show what's happening here I will modify the code a little and add some puts statements, then run it with the first example.
def oscillate(low, steps, increment, length)
high = low + steps * increment
puts "high = #{high}"
n = low
length.times.each_with_object([]) do |_,a|
a << n
diff = (n==low)..(n==high-increment) ? increment : -increment
print "n=#{n}, a<<n=#{a}, diff=#{diff}, "
n += diff
puts "n+=diff=#{n}"
end
end
oscillate(0,3,1,9)
high = 3
n=0, a<<n=[0], diff= 1, n+=diff=1
n=1, a<<n=[0, 1], diff= 1, n+=diff=2
n=2, a<<n=[0, 1, 2], diff= 1, n+=diff=3
n=3, a<<n=[0, 1, 2, 3], diff=-1, n+=diff=2
n=2, a<<n=[0, 1, 2, 3, 2], diff=-1, n+=diff=1
n=1, a<<n=[0, 1, 2, 3, 2, 1], diff=-1, n+=diff=0
n=0, a<<n=[0, 1, 2, 3, 2, 1, 0], diff= 1, n+=diff=1
n=1, a<<n=[0, 1, 2, 3, 2, 1, 0, 1], diff= 1, n+=diff=2
n=2, a<<n=[0, 1, 2, 3, 2, 1, 0, 1, 2], diff= 1, n+=diff=3
#=> [0, 1, 2, 3, 2, 1, 0, 1, 2]
Try this
def oscillate(a, b, step, num)
ramp_up = a.step(b, step).entries
ramp_down = ramp_up.drop(1).reverse.drop(1)
ramp_up.concat(ramp_down).cycle.take(num)
end
How does this work?
creates the ramp_up and ramp_down arrays
concatenates the two arrays
cycle returns an ever-repeating enumerator
take materializes num elements from that enumerator. Other than suggested in a comment, this does not recalculate anything. It just materializes entries from the enumerator.
This is my array:
[[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]]
for which I would like the '0' cells immediately above, below, to the right and left of the 1's to change into 1's as well.
The expected output would be:
0100
1111
0111
0001
However, with my code:
class Image
def initialize(image)
#image = image
end
def output_image
#image.map do |image|
puts image.join('')
end
end
def blur
find_ones.each do |x, y|
blur_cell x, y
end
end
def find_ones
ones = []
#image.each_with_index do |row, y|
row.each_with_index do |cell, x|
ones << [x, y] if cell == 1
end
end
ones
end
def blur_cell(x, y)
write_cell x + 1, y, 1
write_cell x - 1, y, 1
write_cell x, y + 1, 1
write_cell x, y - 1, 1
end
def write_cell(x, y, value)
return nil unless y > 0 && y < #image.length
return nil unless x > 0 && x < #image[0].length
#image[y][x] = value
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
])
image.blur
image.output_image
I am getting this output:
0000
0111
0111
0001
Any help with pointing me to where my error is or any advice on how to fix it would be appreciated :)
Errors in Your Code
Your code had a few small errors. Below is the corrected code. Compare that with your original code, line-by-line, and you'll see the errors and how I've fixed them. I've also made a few simplifications.
class Image
def initialize(image)
#image = image
end
def output_image
#image.map do |image|
puts image.join('')
end
end
def blur
find_ones.each do |x, y|
blur_cell x, y
end
end
def find_ones
ones = []
#image.each_with_index do |row, x|
row.each_with_index do |cell, y|
ones << [x, y] if cell == 1
end
end
ones
end
def blur_cell(x, y)
write_cell x + 1, y
write_cell x - 1, y
write_cell x, y + 1
write_cell x, y - 1
end
def write_cell(x, y)
return unless y >= 0 && y < #image.length
return unless x >= 0 && x < #image[0].length
#image[x][y] = 1 # was reversed
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
])
image.blur
image.output_image
#=> 0100
# 1111
# 0111
# 0001
Suggested alternative
Here's another way of doing that.
def convert(arr)
return [] if arr.empty?
nbr_rows = arr.size
nbr_cols = arr.first.size
a = container(arr)
(1..nbr_rows).
each_with_object(Array.new(nbr_rows) { Array.new(nbr_cols) }) { |i,b|
(1..nbr_cols).each { |j|
b[i-1][j-1] = [a[i][j], a[i][j-1], a[i][j+1], a[i-1][j], a[i+1][j]].max } }
end
def container(arr)
nbr_rows = arr.size
nbr_cols = arr.first.size
Array.new(nbr_rows+2) { |i|
Array.new(nbr_cols+2) { |j| (i.zero? || i==nbr_rows+1 || j.zero? ||
j==nbr_cols+1) ? 0 : arr[i-1][j-1] } }
end
Example
arr = [
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
]
convert arr
#=> [[0, 1, 0, 0],
# [1, 1, 1, 1],
# [0, 1, 1, 1],
# [0, 0, 0, 1]]
Explanation
First observe that an element that equals 0 is set to 1 if the element above, below, left or right equals 1. For elements that are not in the first or last row or first or last column, the calculation is straightforward. One way of dealing with elements on the perimeter is construct a second array that starts with the original array and adds rows of zeros before and aft, and rows of zeros to the left and right. Computations are then made for all elements other than the perimeter rows and columns. Lastly, the first and last rows and first and last columns are stripped away. That's what I've done,
The steps are as follows for the array used in the example. First consider the method container.
nbr_rows = arr.size
#=> 4
nbr_cols = arr.first.size
#=> 4
Array.new(nbr_rows+2) { |i|
Array.new(nbr_cols+2) { |j| (i.zero? || i==nbr_rows+1 || j.zero? ||
j==nbr_cols+1) ? 0 : arr[i-1][j-1] } }
#=> Array.new(6) { |i|
# Array.new(6) { |j| (i.zero? || i==5 || j.zero? ||
# j==5) ? 0 : arr[i-1][j-1] } }
#=> [[0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0, 0],
# [0, 0, 0, 0, 1, 0],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0]]
Notice that this array is arr sandwiched between two rows of zeroes and two columns of zeroes.
Now let's step through convert.
arr.empty?
#=> false, so we do not return []
nbr_rows = arr.size
#=> 4
nbr_cols = arr.first.size
#=> 4
a = container(arr)
#=> [[0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0, 0],
# [0, 0, 0, 0, 1, 0],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0]]
(1..nbr_rows).each_with_object(Array.new(nbr_rows) { Array.new(nbr_cols) }) { |i,b|
(1..nbr_cols).each { |j|
b[i-1][j-1] = [a[i][j], a[i][j-1], a[i][j+1], a[i-1][j], a[i+1][j]].max } }
#=> (1..4).each_with_object(Array.new(4) { [0,0,0,0] }) { |i,b|
# (1..4).each { |j|
# b[i-1][j-1] = [a[i][j], a[i][j-1], a[i][j+1], a[i-1][j], a[i+1][j]].max } }
#=> [[0, 1, 0, 0],
# [1, 1, 1, 1],
# [0, 1, 1, 1],
# [0, 0, 0, 1]]
For readers unfamiliar with Enumerable#each_with_object, the last expression is effectively the same as the following three lines.
b = Array.new(nbr_rows) { Array.new(nbr_cols) }
(1..nbr_rows).each { |i|
(1..nbr_cols).each { |j|
b[i-1][j-1] = [a[i][j], a[i][j-1], a[i][j+1], a[i-1][j], a[i+1][j]].max } }
b
For a Ruby solution, please see #CarySwoveland's answer.
Since your class is called Image and your method name is blur_cell, you might want to check Image Magick and MiniMagick.
The transformation you're looking for is called "diamond dilation".
With ImageMagick, it's as easy as :
convert matrix.png -morphology Dilate Diamond dilated_matrix.png
It converts
to
To convert a matrix to bitmap and vice-versa, you can use
this gem to read and write .pbm files.
I am having trouble dissecting this data. I would like to find out how many #1 are in each list. After finding that number, I would like to append it to another list for later.
I seem to be getting the input:
--> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
When I really want:
--> [2, 2, 2, 2, 0, 2, 2, 1, 11, 0]
This is the code:
d = []
count = 0
b = [[1,3,6,2,7,3,9,2,7,1,7],
[1,5,8,3,0,3,6,2,7,2,1],
[1,5,2,6,8,6,2,5,1,8,9],
[5,2,5,2,1,8,1,5,2,4,6],
[5,7,2,7,3,7,3,7,3,9,2],
[1,5,8,3,0,3,6,2,7,2,1],
[5,2,5,2,1,8,1,5,2,4,6],
[3,6,1,5,7,8,4,3,6,3,3],
[1,1,1,1,1,1,1,1,1,1,1],
[3,4,5,6,8,5,7,5,7,3,7]]
for i in b:
for x in b:
if x == 1:
count =+ 1
d.append(count)
count = 0
print(d)
You are iterating over the wrong object in your second for loop, I believe you meant:
for x in i:
This is why you are getting 0s
There is a Counter class in the standard collections module, so you can simplify this:
>>> from collections import Counter
>>> [Counter(i)[1] for i in b]
[2, 2, 2, 2, 0, 2, 2, 1, 11, 0]
You can also do this without the Counter class a bit more verbosely:
>>> [sum(1 for x in i if x == 1) for i in b]
[2, 2, 2, 2, 0, 2, 2, 1, 11, 0]
I am a beginner working through some exercises. I'm trying to manipulate a 2-dimensional array such that if an element is a 1, then the surrounding non-diagonal elements should be changed to 1:
[[0,0,0,0],
[0,0,1,0],
[0,0,0,0],
[0,0,0,0]]
should return
[[0,0,1,0],
[0,1,1,1],
[0,0,1,0],
[0,0,0,0]]
I am running into problems using nested each_with_index: After I adjust initial changes for the surrounding left and right, as the method iterates it picks up by earlier adjustment and makes an unwanted change. Moreover, the line which should change the "bottom" element is throwing an error:
a = [[0,0,0,0],
[0,0,1,0],
[0,0,0,0],
[0,0,0,0]
]
a.each_with_index do |m, n| # n == index of main array
m.each_with_index do |x, y| # y == index of subarray
if x == 1
a[n][y+1] = 1 unless (a[n][y+1]).nil? #right
a[n][y-1] = 1 unless (a[n][y-1]).nil? #left
a[n-1][y] = 1 unless (a[n-1][y]).nil? #top
a[n+1][y] = 1 unless (a[n+1][y]).nil? #bottom--currently giving an error
end
end
end
Any suggestions as to how I can go about solving these two aspects will be well received.
In order to avoid interference of a previous step, you can either (deep) duplicate the array and separate the reference array from the modifying one, or extract all the relevant indices before modifying the array. The latter is better. Also, using a flat array is much easier than handling a nested array, so I will convert a to and from a flattened array b, and work within b.
b = a.flatten
b
.each_index.select{|i| b[i] == 1}
.each do
|i|
b[i - 1] = 1 if b[i - 1] and i - 1 >= 0
b[i + 1] = 1 if b[i + 1]
b[i - 4] = 1 if b[i - 4] and i - 4 >= 0
b[i + 4] = 1 if b[i + 4]
end
a = b.each_slice(4).to_a
# => [[0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 0, 0]]
I suggest you use the Matrix class.
require 'matrix'
m = Matrix[*a]
#=> Matrix[[0, 0, 0, 0],
# [0, 0, 1, 0],
# [0, 0, 0, 0],
# [0, 0, 0, 0]]
row, col = m.index(1)
#=> [1, 2]
Matrix.build(m.row_size, m.column_size) { |r,c|
(c-col).abs + (r-row).abs <= 1 ? 1 : 0 }.to_a
#=> [[0, 0, 1, 0],
# [0, 1, 1, 1],
# [0, 0, 1, 0],
# [0, 0, 0, 0]]
The non-matrix version of this (which uses the methods Array#index, Fixnum#divmod, Array::new, Enumerable#each_slice, and a few others) is as follows.
nrows, ncols = a.size, a.first.size
#=> [4, 4]
row, col = a.flatten.index(1).divmod(ncols)
#=> [1, 2]
Array.new(nrows*ncols) do |i|
r, c = i.divmod(ncols)
(c-col).abs + (r-row).abs <= 1 ? 1 : 0
end.each_slice(ncols).to_a
#=> [[0, 0, 1, 0],
# [0, 1, 1, 1],
# [0, 0, 1, 0],
# [0, 0, 0, 0]]
I find the method using the Matrix class to be easier to understand, though it may not be as efficient.