LoginSignup
0
0

More than 1 year has passed since last update.

[Ruby] 配列の結合と展開

Posted at

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

元のコード

今回は下記のコードをリファクタして、ベンチマークを計測しながら比べていこうと思います。
計測方法はこちらの記事を参考に計測していきます。

def nomal(items)
  name_array = []

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

  name_array.flatten
end

計測コード

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

def each_splat(items)
  name_array = []

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

  name_array
end

map.flatten

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

flat_map

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

ベンチマーク

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型は比較的に改善されない結果となりました。
また、この記事に書かれてある通り、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 が選ばれます。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0