[Ruby] 配列の結合と展開

Rubyによる、配列を結合し展開する処理について調べていみました。
間違っていればご指摘お願いします！

# 元のコード
今回は下記のコードをリファクタして、ベンチマークを計測しながら比べていこうと思います。
計測方法は[こちらの記事](https://qiita.com/scivola/items/c5b2aeaf7d67a9ef310a)を参考に計測していきます。
```ruby
def nomal(items)
  name_array = []

  items.each do |item|
    name_array.push([item[:id], item[:name]])
  end

  name_array.flatten
end
```

## 計測コード
```ruby
require 'benchmark'

n = 10
items = 100.times.flat_map do |i|
          [id: i, name: "山田#{i}"]
        end

Benchmark.bm(10) do |r|
  r.report "nomal" do
    n.times{ nomal(items) }
  end

  r.report "each_splat" do
    n.times{ each_splat(items) }
  end

  r.report "map_flatten" do
    n.times{ map_flatten(items) }
  end

  r.report "flat_map" do
    n.times{ flat_map(items) }
  end
end
```

# 各リファクタコード
## each.splat
```ruby
def each_splat(items)
  name_array = []

  items.each do |item|
    name_array.push(*[item[:id], item[:name]])
  end

  name_array
end
```

## map.flatten
```ruby
def map_flatten(items)
  name_array = items.map do |item|
                 [item[:id], item[:name]]
               end.flatten
end
```

## flat_map
```ruby
def flat_map(items)
  name_array = items.flat_map do |item|
                  [item[:id], item[:name]]
                end
end
```

# ベンチマーク
```ruby
n = 1
user     system      total        real
nomal        0.000075   0.000004   0.000079 (  0.000073)
each_splat   0.000051   0.000001   0.000052 (  0.000052)
map_flatten  0.000056   0.000001   0.000057 (  0.000056)
flat_map     0.000055   0.000001   0.000056 (  0.000057)

n = 3
user     system      total        real
nomal        0.000098   0.000002   0.000100 (  0.000098)
each_splat   0.000058   0.000001   0.000059 (  0.000059)
map_flatten  0.000123   0.000005   0.000128 (  0.000136)
flat_map     0.000048   0.000005   0.000053 (  0.000051)

n = 10
user     system      total        real
nomal        0.000641   0.000003   0.000644 (  0.000640)
each_splat   0.000244   0.000009   0.000253 (  0.000252)
map_flatten  0.000436   0.000006   0.000442 (  0.000442)
flat_map     0.000238   0.000008   0.000246 (  0.000247)

n = 100
user     system      total        real
nomal        0.005138   0.000109   0.005247 (  0.005243)
each_splat   0.002081   0.000009   0.002090 (  0.002089)
map_flatten  0.003699   0.000031   0.003730 (  0.003730)
flat_map     0.001785   0.000001   0.001786 (  0.001787)

n = 1000
user     system      total        real
nomal        0.037624   0.000269   0.037893 (  0.038009)
each_splat   0.014614   0.000028   0.014642 (  0.014689)
map_flatten  0.030695   0.000288   0.030983 (  0.031110)
flat_map     0.015104   0.000052   0.015156 (  0.015213)

n = 100000
user     system      total        real
nomal        3.133017   0.005101   3.138118 (  3.144279)
each_splat   1.461549   0.002172   1.463721 (  1.465885)
map_flatten  2.816710   0.003754   2.820464 (  2.825173)
flat_map     1.506553   0.001913   1.508466 (  1.510696)
```

# まとめ
パフォーマンスという観点では、 **each.splat型** と **flat_map型** が約50%も改善されており、map.flatten型は比較的に改善されない結果となりました。
また、[この記事](https://techracho.bpsinc.jp/hachi8833/2020_11_06/59639)に書かれてある通り、Rubyではeachよりもmapなどのコレクションを積極的に使うことが推奨されています。
 **「ループ内に処理を逐一記述する」** という手続き的発想から **「処理をEnumerableのメソッドに渡す」** というRubyらしい発想が大事らしいです。
つまり、今回のリファクタでは、 **flat_map** を用いて下記の通りにリファクタすることが良さそうです！
```
def flat_map(items)
  name_array = items.flat_map do |item|
                  [item[:id], item[:name]]
                end
end
```

# 補足
map.flattenも場合によっては、選ばれる実装方法です。flattenは、flat_mapと異なり、一階層展開するのではなく全て展開します。例えば、二次元配列から一次元配列を得るのではなく、多次元配列から一次元配列を得る場合、 **flatten** が選ばれます。