wikiti

8/21/2015 - 1:13 AM

Print or traverse a matrix in spiral order using Ruby language

```
# Helper method to clone objects
def deep_clone(object)
Marshal.load(Marshal.dump(object))
end
# The concept y pretty simple: imagine that you have the following matrix:
# 1 2 3 4
# 10 11 12 5
# 9 8 7 6
#
# Represented in ruby like so:
# [[1,2,3,4],[10,11,12,5],[9,8,7,6]]
#
# The result of traveling it in spiral order should be [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
#
# The trick is to "peel layers" from the matrix's borders (top, right, botto and left) until it's fully empty.
#
# TOP LAYER
# output: [1, 2, 3, 4]
# 1 2 3 4
# | | | |
# 10 11 12 5 10 11 12 5
# 9 8 7 6 9 8 7 6
# --------------------------------
# RIGHT LAYER
# output: [1, 2, 3, 4, 5, 6]
#
# 10 11 12 - 5 10 11 12
# 9 8 7 - 6 9 8 7
# --------------------------------
# BOTTOM LAYER
# output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
#
# 10 11 12 10 11 12
# | | |
# 9 8 7
# --------------------------------
# LEFT LAYER
# output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#
# 10 - 11 12 11 12
# --------------------------------
# TOP LAYER
# output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
#
# 11 12
# ^ ^
# --------------------------------
# END
# output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
#
# Note that the borders from left and bottom layers must be reversed in order to traverse them properly.
#
def spiral_matrix(matrix)
# Copy the matrix (optional) and prepare the output array.
matrix = deep_clone(matrix)
output = []
# Lambdas to execute depending on the current case. In summary, they will do the following:
# top -> remove the first array, which is the first row.
# right -> for each row, remove the last element (last column) and return all of them packed (map).
# bottom -> remove the last array, which is the last row. The result must be reversed.
# left -> for each row, remove the first element (first column) and return all of them packed (map). The result must be reversed.
actions = {
top: lambda{ matrix.shift },
right: lambda{ matrix.map { |f| f.pop } },
bottom: lambda{ matrix.pop.reverse },
left: lambda{ matrix.map { |f| f.shift }.reverse }
}
# `cases` will iterate the above hash keys like following: top, right, bottom, left, top, right, ...
cases = actions.keys.cycle
# Repeat until the matrix is empty (this will call the lambdas from the hash of above).
output.concat actions[ cases.next ].call() until matrix.empty?
# Return output array.
output
end
# Test it!
matrix = [
[ 1, 2, 3, 4],
[12, 13, 14, 5],
[11, 16, 15, 6],
[10, 9, 8, 7]
]
puts spiral_matrix(matrix).join(', ') # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
```