T5i
T5i

Reputation: 1530

Convert an index into coordinates

When I have an array (of 1D, 2D, 3D, or more dimensions), I would like to be able to translate an index (from this flatten array) into its coordinates.

For instance, considering * square:

3D array (of [2,3,2] shape)

arr = [ [ [ nil, nil ],
          [ nil, nil ],
          [ nil, nil ] ],
        [ [ "*", nil ],
          [ nil, nil ],
          [ nil, nil ] ] ]

arr.flatten[6]           # => "*"
arr.index2coordinates(6) # => [1,0,0] or [1][0][0]

4D array (of [2,3,1,2] shape)

arr = [ [ [ [ nil, nil ],
            [ nil, "*" ],
            [ nil, nil ] ] ],
        [ [ [ nil, nil ],
            [ nil, nil ],
            [ nil, nil ] ] ] ]

arr.flatten[3]           # => "*"
arr.index2coordinates(3) # => [0,0,1,1] or [0][0][1][1]

1D array (of [5] shape)

arr = [ nil, nil, nil, "*", nil ]

arr.flatten[3]           # => "*"
arr.index2coordinates(3) # => [3]

How can we do a such Array#index2coordinates method? In a sense, this question is the inverse of Convert vector to integer question. Thanks a lot.

Upvotes: 2

Views: 1034

Answers (2)

maerics
maerics

Reputation: 156434

Try a recursive search through the structure building the n-dimensional index as you enter each step, returning the index if/when you find the * target:

def coord(space, target='*', idx=[])
  found = nil
  space.each_with_index do |x,i|
    found = if x.is_a?(Array)
      coord(x, target, idx + [i])
    elsif x == target
      idx + [i]
    end
    break found if found
  end
  found
end

coord(arr3d) # => [1, 0, 0]
coord(arr4d) # => [0, 0, 1, 1]
coord(arr1d) # => [3]
coord([]) # => nil

Upvotes: 0

andrykonchin
andrykonchin

Reputation: 2517

Examples, step by step:

# 3D array (of [2,3,2] shape)
arr.flatten[6]           # => "*"
arr.index2coordinates(6) # => [1,0,0] or [1][0][0]

6 % 2              # => 0

(6 / 2) % 3        # (3) % 3 => 0

((6 / 2) / 3) % 2  # ((3) / 3) %2 => 1 % 2 => 1


# 4D array (of [2,1,3,2] shape)    
arr.flatten[3]           # => "*"
arr.index2coordinates(3) # => [0,0,1,1] or [0][0][1][1]

3 % 2                     # => 1

(3 / 2) % 3               # => 1 % 3 => 1

((3 / 2) / 3) % 1         # => 1 % 1 => 0

(((3 / 2) / 3) / 1) % 2   # 0 % 2 => 0

Simple code for second example

result = []
index = 3

[2,1,3,2].reverse.each do |dimension|
  result = [index % dimension] + result
  index = (index / dimension)
end

result          # => [0, 0, 1, 1]

Upvotes: 3

Related Questions