Kannan Ekanath
Kannan Ekanath

Reputation: 17631

Ruby getting the diagonal elements in a 2d Array

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

Answers (4)

schmijos
schmijos

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

YOU
YOU

Reputation: 123897

puts (0..2).collect { |i| array[i][i] }

Upvotes: 19

Shai
Shai

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

zilla
zilla

Reputation: 952

Better might be a one-liner that utilizes the Matrix library:

require 'matrix'
Matrix.rows(array).each(:diagonal).to_a

Upvotes: 11

Related Questions