rubyで、テーブルのデータの合計値を算出する



# 課題
## テーブルのセルの[列&行]を合算する
![スクリーンショット 2022-10-05 18.15.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492674/23722c02-0930-6eab-e87b-8a488b9b3240.png)

# 実装方法
1. テーブルを作成
1. 行の合計値を出す
1. 列の合計値を出す
1. テーブルに、行の合計値を足す
1. テーブルに、列の合計値を足す

```rb
table = [
    [9, 85, 92, 20],
    [68, 25, 80, 55],
    [43, 96, 71, 73],
    [43, 19, 20, 87],
    [95, 66, 73, 62]
]

line_sum = table.map(&:sum)
p line_sum
column_sum = table.transpose.map(&:sum)
p column_sum

p table.concat([column_sum])
table_sum = table.each_with_index{|line,index|
    line.push(line_sum[index])
}
p table_sum
```

# 結果
```
# 行の合計
[206, 228, 283, 169, 296]
# 列の合計
[258, 291, 336, 297]
# 列を合計したテーブルの値
[[9, 85, 92, 20], [68, 25, 80, 55], [43, 96, 71, 73], [43, 19, 20, 87], [95, 66, 73, 62], [258, 291, 336, 297]]
# 最終的なテーブルの値
[[9, 85, 92, 20, 206], [68, 25, 80, 55, 228], [43, 96, 71, 73, 283], [43, 19, 20, 87, 169], [95, 66, 73, 62, 296], [258, 291, 336, 297, nil]]
```

# コメント

以上のようなコードになるのですが、

- ダメな所
- もっと効率の良い書き方
 
をレビューして頂けないでしょうか？


# レビューを受けての変更後

@scivolaさんのコメントを考慮して変更したコード
> このコードだと右下の角の値（合計の合計）が nil になりますね
> 最初に与えられた table の値を破壊してしまうのは，要件によってはまずいですね
> column_sum から配列を作って concat しなくても


```rb
origin_table = [
    [9, 85, 92, 20],
    [68, 25, 80, 55],
    [43, 96, 71, 73],
    [43, 19, 20, 87],
    [95, 66, 73, 62]
]
# 元のテーブルを壊さないように、複製する
dup_table = Marshal.load(Marshal.dump(origin_table))

line_sum = dup_table.map(&:sum)
p line_sum
column_sum = dup_table.transpose.map(&:sum)
p column_sum

dup_table << column_sum

sum_table = dup_table.each_with_index{|line,index|
    line << line_sum[index]
}

# 最終行、最終列がnilになっている事を確認
pp sum_table[-1][-1]
puts
# 最終行のnilを取り除くとsum出来る
sum_table[-1][-1] = sum_table[-1].compact.sum

pp sum_table
puts
# 元のテーブルに変更がない事を確認
pp origin_table
```

## 結果
![スクリーンショット 2022-10-06 11.02.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492674/f7e2a4a4-6a60-6717-d307-8ecabb8ef7b5.png)

# テーブルの複製に関して

@scivola さんより指摘を頂き、テーブルを複製する際は、`Marshal`クラスを使うのではなくて、
Arrayクラスの`dup`を使う方が有用である事がわかりました
https://docs.ruby-lang.org/ja/latest/method/Object/i/clone.html

dupでコピーをする際は、深さを意識してコピーをする必要があります

> clone や dup はオブジェクト自身を複製するだけで、オブジェクトの指している先(たとえば配列の要素など)までは複製しません。これを浅いコピー(shallow copy)といいます。

なので、`dup`でコピーする際は、配列の要素に対して、`dup`を使う必要があります

## [例] 深いコピー ※配列の要素に対して、dupを実行する
```rb
origin_table = [
    [9, 85, 92, 20],
    [68, 25, 80, 55],
    [43, 96, 71, 73],
    [43, 19, 20, 87],
    [95, 66, 73, 62]
]
# 元のテーブルを壊さないように、複製する
dup_table = origin_table.map{ |row| row.dup } # 深い
# dup_table = origin_table.dup # 浅い

# 同じオブジェクトか確認
p(origin_table.equal?(dup_table))

line_sum = dup_table.map(&:sum)
p line_sum
column_sum = dup_table.transpose.map(&:sum)
p column_sum

dup_table << column_sum

sum_table = dup_table.each_with_index{|line,index|
    line << line_sum[index]
}
```
## [深いコピー]結果
![スクリーンショット 2022-10-07 11.53.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492674/f6bc5c8e-b0fb-6025-0e59-89993e11d08b.png)

## [例] 浅いコピー ※配列の要素に対しては、dupをしない
```rb
origin_table = [
    [9, 85, 92, 20],
    [68, 25, 80, 55],
    [43, 96, 71, 73],
    [43, 19, 20, 87],
    [95, 66, 73, 62]
]
# 元のテーブルを壊さないように、複製する
# dup_table = origin_table.map{ |row| row.dup } # 深い
dup_table = origin_table.dup # 浅い
# 同じオブジェクトか確認
p(origin_table.equal?(dup_table))

line_sum = dup_table.map(&:sum)
p line_sum
column_sum = dup_table.transpose.map(&:sum)
p column_sum

dup_table << column_sum

sum_table = dup_table.each_with_index{|line,index|
    line << line_sum[index]
}

# 最終行、最終列がnilになっている事を確認
pp sum_table[-1][-1]
puts
# 最終行のnilを一度取り除くとsum出来る
sum_table[-1][-1] = sum_table[-1].compact.sum

pp sum_table
puts
# 元のテーブルに変更がない事を確認
pp origin_table
```

## [浅いコピー]結果 ※要素に対してはdupを行なっていないので、コピー元が影響を受けている
![スクリーンショット 2022-10-07 11.55.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492674/cf388f0b-44f7-8830-66a3-f79bf6b3f252.png)

# 簡単に実装できる事が判明、、
@obelisk68　さんのコメント通り試したら、簡単に実装出来ました、、

```rb
table = [
    [9, 85, 92, 20],
    [68, 25, 80, 55],
    [43, 96, 71, 73],
    [43, 19, 20, 87],
    [95, 66, 73, 62]
  ]
  # mapで行を回して、合計値を足す
  tmp = table.map { |row| row + [row.sum] }
  puts 
  # transposeで行&列を入れ替えて合計し、元に戻す
  pp result = tmp.transpose.map { |col| col + [col.sum] }.transpose
```

## 個人的反省点

1. 経験値が低すぎるのかも知れない、、
→ コードを読む量　& 書く量が少なくて、実装方法が分かってない

## 上の解決策

1. たくさんコードを書いたり、読んだりする経験を増やす
1. paiza等で、プログラミングの問題を解く
→有用じゃないかと思ってる。
実際にコードを書いて実装してみないと
・分からない所
・分かってない所
に気づけないので

# テストコード付きで書き直してみた

テストがないので、コードを変更する度に、動作確認するのが大変だった。
テストも出来るように、書き直してみました。
※こちらもレビュー頂けるとありがたいです
https://github.com/syo-tokeshi/sum-table-by-ruby

## 実行クラス
```rb
class Table
    @@table = [
        [9, 85, 92, 20],
        [68, 25, 80, 55],
        [43, 96, 71, 73],
        [43, 19, 20, 87],
        [95, 66, 73, 62]
    ]

    def self.get_table_data
        @@table
    end

    def self.genarate_table
        add_line_sum
        column_sum
        @@table
    end

    # mapで行を回して、合計値を足す
    def self.add_line_sum
        @@table = @@table.map { |row| row + [row.sum] }
    end
    
    # transposeで行&列を入れ替えて合計し、元に戻す
    def self.column_sum
        @@table = @@table.transpose.map { |col| col + [col.sum] }.transpose
    end
end

# pp Table.genarate_table
```

## テストクラス
```rb
require './table.rb'
require 'minitest/autorun'

class TableTest < Minitest::Test

  # テーブルのデータが有効か確認
  def test_valid_table_data
    assert Table.get_table_data
  end

  # 行の合計を計算出来るか
  def test_add_line_sum
    assert_equal 206,Table.add_line_sum[0][-1]
  end

  # 列の合計を計算出来るか
  def test_column_sum
    assert_equal 258,Table.column_sum[-1][0]
  end

  # 合計したテーブルが作れることを確認
  def test_genarate_table
    assert Table.genarate_table
  end

end
```

テストに通れば、4項目クリア出来ます
```
% ruby "/Users/admin/tokeshi_free_space/test_table.rb"
Run options: --seed 22557

# Running:

....

Finished in 0.001053s, 3798.6693 runs/s, 3798.6693 assertions/s.

4 runs, 4 assertions, 0 failures, 0 errors, 0 skips
```

# 参考文献
minitest公式サイト
https://www.rubydoc.info/gems/minitest

配列のコピーについて
https://mistyrinth.hatenablog.com/entry/2019/03/04/173445