10

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?}

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Kannan Ekanath
  • 16,759
  • 22
  • 75
  • 101
  • 1
    You probably know this, but `puts array[1..2][1]` is equivalent to `puts array[2]`, not `puts array[1..2].map{|arr| arr[1]}`. I'm not sure if using arrays of arrays it the right way of doing things, but I haven't been able to build anything better. – Andrew Grimm Mar 24 '10 at 22:42
  • That was my intention. But your answer is still very useful. Thanks :) – Kannan Ekanath Mar 24 '10 at 23:00

4 Answers4

19
puts (0..2).collect { |i| array[i][i] }
Dorian
  • 22,759
  • 8
  • 120
  • 116
YOU
  • 120,166
  • 34
  • 186
  • 219
9

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

require 'matrix'
Matrix.rows(array).each(:diagonal).to_a
zilla
  • 909
  • 1
  • 11
  • 17
3

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)
Community
  • 1
  • 1
Shai
  • 1,281
  • 9
  • 7
1

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]]
schmijos
  • 8,114
  • 3
  • 50
  • 58