## Code katas are so humbling

30 Apr 2017I have been working through, slowly, the ruby track on exercism. It’s a great site which I highly reccommend. Anyway, todays task was to identify saddle points. Here… I’ll print the readme

### Saddle point README

```
# Saddle Points
Detect saddle points in a matrix.
So say you have a matrix like so:
0 1 2
|---------
0 | 9 8 7
1 | 5 3 2 <--- saddle point at (1,0)
2 | 6 6 7
It has a saddle point at (1, 0).
It's called a "saddle point" because it is greater than or equal to
every element in its row and the less than or equal to every element in
its column.
A matrix may have zero or more saddle points.
Your code should be able to provide the (possibly empty) list of all the
saddle points for any given matrix.
```

You are then given a spec file

```
#!/usr/bin/env ruby
gem 'minitest', '>= 5.0.0'
require 'minitest/autorun'
require_relative 'saddle_points'
class MatrixTest < Minitest::Test
def test_extract_a_row
matrix = Matrix.new("1 2\n10 20")
assert_equal [1, 2], matrix.rows[0]
end
def test_extract_same_row_again
matrix = Matrix.new("9 7\n8 6")
assert_equal [9, 7], matrix.rows[0]
end
def test_extract_other_row
matrix = Matrix.new("9 8 7\n19 18 17")
assert_equal [19, 18, 17], matrix.rows[1]
end
def test_extract_other_row_again
matrix = Matrix.new("1 4 9\n16 25 36")
assert_equal [16, 25, 36], matrix.rows[1]
end
def test_extract_a_column
matrix = Matrix.new("1 2 3\n4 5 6\n7 8 9\n 8 7 6")
assert_equal [1, 4, 7, 8], matrix.columns[0]
end
def test_extract_another_column
matrix = Matrix.new("89 1903 3\n18 3 1\n9 4 800")
assert_equal [1903, 3, 4], matrix.columns[1]
end
def test_no_saddle_point
matrix = Matrix.new("2 1\n1 2")
assert_equal [], matrix.saddle_points
end
def test_a_saddle_point
matrix = Matrix.new("1 2\n3 4")
assert_equal [[0, 1]], matrix.saddle_points
end
def test_another_saddle_point
matrix = Matrix.new("18 3 39 19 91\n38 10 8 77 320\n3 4 8 6 7")
assert_equal [[2, 2]], matrix.saddle_points
end
def test_multiple_saddle_points
matrix = Matrix.new("4 5 4\n3 5 5\n1 5 4")
assert_equal [[0, 1], [1, 1], [2, 1]], matrix.saddle_points
end
end
```

And then you have to give your own solution.

Mine was, frankly, horrible. I was slightly stymied as I had to keep the `rows`

and `column`

methods as they were. I ended up making a terribly verbose class, creating a `Point`

class etc.

I knew it was horrible too, the `@rows`

shadowing the `rows`

method (which returned values), it was horrible. If I’d been able to refactor the tests I think I could have cleared it up quite a bit more nicely. I did like my little `Point`

value object though, that was nice.

## My solution

```
class Matrix
def initialize(matrix_string)
@rows = matrix_string
.lines
.map(&:strip)
.map(&:split)
.map { |row| row.map(&:to_i) }
@rows = @rows.map.with_index do |cols, row_index|
cols.map.with_index do |value, col_index|
Point.new(row_index, col_index, value)
end
end
@columns = @rows.transpose
end
def saddle_points
max_row_elements.select do |point|
col_for_point = @columns[point.col]
col_for_point.all? { |p| (point.value <= p.value) || p == point } ? point : nil
end.flatten.compact.map(&:to_coordinates)
end
def rows
@rows.map do |row|
row.map(&:value)
end
end
def columns
@columns.map do |column|
column.map(&:value)
end
end
private
def max_row_elements
@max_row_elements ||= begin
@rows.map do |row|
max_value_in_row = row.max.value
row.select {|p| p.value == max_value_in_row}
end
end.flatten
end
class Point < Struct.new(:row, :col, :value)
def <=>(other)
value <=> other.value
end
def ==(other)
row == other.row && col == other.col
end
def to_coordinates
[row, col]
end
end
end
```

Anyway, after looking at other peoples solutions, I saw that the proper way to do it was to just neatly iterate over all the positions in the Matrix, and that the easiest way to do that was to use the `each_index`

method for the rows & columns combined with the `product`

method.

Obvious in retrospect.. looks like this:

```
class Matrix
attr_reader :rows, :columns
def initialize(string)
@rows = string.each_line.map { |line| line.split.map(&:to_i) }
@columns = @rows.transpose
end
def saddle_points
all_coordinates.select do |i, j|
rows[i].max == rows[i][j] && columns[j].min == rows[i][j]
end
end
private
def all_coordinates
rows.each_index.to_a.product(columns.each_index.to_a)
end
end
```

Really quite a lot nicer, and infinitely shorter.

Well, I highly recommend exercism, it’s really good.