Rubyで開発をしていると配列からハッシュ(連想配列)を生成するコードを書くことがあります。
個人的にはreduce関数を使ってバシッと書くのが気持ちよくて好きなのですが、最近、人によってこの処理の書き方が違うことに気づきました。
4つの書き方
僕が個人的に観測しているのは以下の4つの書き方(方法)です。
- for inを使った方法
- each関数を使った方法
- each_with_object関数を使った方法
- reduce関数を使った方法
それぞれ、簡単なコードを再現してみました。
処理は非常にシンプルで配列の各要素を文字列にした値をハッシュのキーとし、各要素を2倍にした値をバリューとします。
for inを使った方法
list = (1..10000).to_a hash = {} for n in list do hash[n.to_s] = n * 2 end
each関数を使った方法
list = (1..10000).to_a hash = {} list.each do |n| hash[n.to_s] = n * 2 end
each_with_object関数を使った方法
list = (1..10000).to_a list.each_with_object({}) do |n, hash| hash[n.to_s] = n * 2 end
reduce関数を使った方法
list = (1..10000).to_a list.reduce({}) do |hash, n| hash[n.to_s] = n * 2; hash end
では、この4つのコードのパフォーマンスを計測してみます。
計測結果
計測には標準ライブラリのbenchmark
を使用しました。単位は秒です。
計測に使用したコードはこちらにて公開しているので、気になる方は覗いてみてください。
user system total real for in: 0.003110 0.000189 0.003299 ( 0.003295) each: 0.001693 0.000066 0.001759 ( 0.001761) each_with_object: 0.002402 0.000057 0.002459 ( 0.002477) reduce: 0.002407 0.000021 0.002428 ( 0.002430)
計測結果を見てみると、4つの方法の実行時間にはあまり差がないことが分かります。
ただ、system
時間を見てみるとreduce関数が最も短く、for inが最も時間が長いようですが、内部実装に違いがあるのでしょうか。JavaScriptで連番の配列の生成速度を比較した時は逆の結果になりました。
非常に興味深い結果ですが、Rubyのコードを深掘りしないと答えには辿り着けなさそうです。
勝手にeach関数はfor inもしくはwhile文を内部で呼び出していると思っていたので、each関数が最も速いという結果が出たことには驚きました。その他のeach_with_object・reduce関数はほぼ誤差なので、好みの問題というレベルでした。
結論
for in以外なら好きなものを使えば良いと思います。
今回はメモリ使用率やCPU負荷については計測していないのですが、この程度のデータ量であれば、大きな違いはないでしょう。チームで「この書き方にしよう」と決めれば何でも良いですね。個人的には変数宣言も不要で、汎用性が高いreduce関数を推していきます。each_with_object関数は便利ですが、このためだけに1つ関数を覚えないといけないというのが個人的には「何だかな...」と感じるポイントです。
少しでも「ええな〜」と思ったらイイネ!・シェア!・はてなブックマークを頂けると励みになります。