tags:

views:

719

answers:

5

I wonder if there's a possibility to create a two dimensional array and to quickly access any horizontal or vertical sub array in it? Ok, I believe we can access a horizontal sub array in the following case:

x = Array.new(10)
10.times do x << Array.new(20)

x[6][3..8] = 'something'

But as far as I understand, we cannot access like this x[3..8][6]? How to avoid (or hack) this limit?

A: 

rows, cols = x,y (your values) grid = Array.new(rows) { Array.new(cols) }

Try it out and play around, Ruby quite fun :)

As for accessing elements, this article is pretty good for step by step way to encapsulate an array in the way you want:

How to ruby array

shawnjan
+1  A: 

x.transpose[6][3..8] or x[3..8].map {|r| r [6]} would give what you want.

Example:

a = [ [1,  2,  3,  4,  5],
      [6,  7,  8,  9,  10],
      [11, 12, 13, 14, 15],
      [21, 22, 23, 24, 25]
    ]

#a[1..2][2]  -> [8,13]
puts a.transpose[2][1..2].inspect   # [8,13]
puts a[1..2].map {|r| r[2]}.inspect  # [8,13]
pierr
Oh, Array inherited `transpose`? Great!
gmile
The problem with this is: transpose is O(n*m), but retrieving a subarray in one direction can be in O(n+m)
johannes
using `collect` instead of `map` adds a bit of clarity here.
glenn jackman
As I understood it, map and collect are the same. It's just which name you prefer for this task.
johannes
+2  A: 

There are some problems with 2 dimensional Arrays the way you implement them.

a= [[1,2],[3,4]]
a[0][2]= 5 # works
a[2][0]= 6 # error

Hash as Array

I prefer to use Hash's for multi dimensional Array's

a= Hash.new
a[[1,2]]= 23
a[[5,6]]= 42

This has the advantage, that you don't have to manually create collumns or rows. Inserting into hashes is almost O(1), so there is no drawback here, as long as your Hash does not bekome to big.

You can even set a default value for all not specified elements

a= Hash.new(0)

So now about how to get subarray's

(3..5).to_a.product([2]).collect { |index| a[index] }
[2].product((3..5).to_a).collect { |index| a[index] }

(a..b).to_a runs in O(n). Retrieving an element from an Hash is almost O(1), so the collect runs in almost O(n). There is now way to make it faster than O(n), as copying n elements always is O(n).

Hash's can have problems when they are getting to big. So I would think twice about implementing a multidimensional Array like this, if I knew my amount of data is getting big.

johannes
+2  A: 

You didn't state your actual goal, but maybe this can help:

require 'matrix'  # bundled with Ruby
m = Matrix[
 [1, 2, 3],
 [4, 5, 6]
]

m.column(0) # ==> Vector[1, 4]

(and Vectors acts like arrays)

or, using a similar notation as you desire:

m.minor(0..1, 2..2) # => Matrix[[3], [6]]
Marc-André Lafortune
If you need additional functionality or prefer to use the `x[6][3..8]` notation, you can always subclass `Matrix` and extend it.
bta
A: 

Here's a 3D array case

class Array3D
   def initialize(d1,d2,d3)
    @data = Array.new(d1) { Array.new(d2) { Array.new(d3) } }
   end

  def [](x, y, z)
    @data[x][y][z]
  end

  def []=(x, y, z, value)
    @data[x][y][z] = value
  end
end

You can access subsections of each array just like any other Ruby array. @data[0..2][3..5][8..10] = 0 etc

Mark Essel