Reputation: 17631
I was trying some problems with my 2D ruby array and my LOC reduces a lot when I do array slicing. So for example,
require "test/unit"
class LibraryTest < Test::Unit::TestCase
def test_box
array = [[1,2,3,4],[3,4,5,6], [5,6,7,8], [2,3,4,5]]
puts array[1][2..3] # 5, 6
puts array[1..2][1] # 5, 6, 7, 8
end
end
I want to know if there is a way to get a diagonal slice? Lets say I want to start at [0,0] and want a diagonal slice of 3. Then I would get elements from [0,0], [1,1], [2,2] and I will get an array like [1,4,7] for example above. Is there any magic one-liner ruby code that can achieve this? 3.times do {some magic stuff?}
Upvotes: 11
Views: 7475
Reputation: 8745
I'm picking up @Shai's answer and propose to make it more functional.
First we initialize the array:
arr = [
[1, 2, 3, 4],
[3, 4, 5, 6],
[5, 6, 7, 8],
[2, 3, 4, 5]
]
Then we prepare the array which serves as padding:
padding = [*0..(arr.length - 1)].map { |i| [nil] * i }
=> [
[],
[nil],
[nil, nil],
[nil, nil, nil]
]
Then we apply the padding to the array. If you reverse the first usage of the padding or the second one depends if you want to get downward or upward diagonals.
padded = padding.reverse.zip(arr).zip(padding).map(&:flatten)
=> [
[nil, nil, nil, 1, 2, 3, 4],
[nil, nil, 3, 4, 5, 6, nil],
[nil, 5, 6, 7, 8, nil, nil],
[2, 3, 4, 5, nil, nil, nil]
]
Then we transpose as in @Shai's solution:
padded.transpose.map(&:compact)
=> [
[2],
[5, 3],
[3, 6, 4],
[1, 4, 7, 5],
[2, 5, 8],
[3, 6],
[4]
]
Upvotes: 3
Reputation: 1529
Ruby snippet based off of Get all the diagonals in a matrix/list of lists in Python
This is to get all the diagonals. Anyway, the idea is to pad the array from different sides so the diagonals align up in the rows and columns:
arr = [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [2, 3, 4, 5]]
# pad every row from down all the way up, incrementing the padding.
# so: go through every row, add the corresponding padding it should have.
# then, grab every column, that’s the end result.
padding = arr.size - 1
padded_matrix = []
arr.each do |row|
inverse_padding = arr.size - padding
padded_matrix << ([nil] * inverse_padding) + row + ([nil] * padding)
padding -= 1
end
padded_matrix.transpose.map(&:compact)
Upvotes: 3
Reputation: 952
Better might be a one-liner that utilizes the Matrix library:
require 'matrix'
Matrix.rows(array).each(:diagonal).to_a
Upvotes: 11