こんにちは!ジョンです。
今回はRailsの便利メソッドのひとつ、 map
と pluck
の使い分け・違いについて紹介記事を書いてみようと思います。
Railsエンジニアなら1度は見たことあるであろうこのメソッドですが、この記事で更に興味を持っていただけると嬉しいです!
mapについて
簡単に言えば、オブジェクトに対して処理を行い、Arrayにまとめるメソッドです。
例えば、
Fruitテーブル
id | name |
---|---|
1 | りんご |
2 | バナナ |
3 | みかん |
4 | メロン |
5 | スイカ |
Fruit.all.map do |item|
fruit.name
end
=> [ "りんご", "バナナ", "みかん", "メロン", "スイカ" ]
余談
Fruit.all.map(&:name)
と書くと1行でまとめられます。
pluckについて
Rails(ActiveRecord)に用意されている、指定したカラムのレコードの配列を取得するメソッドです。
上記の例をそのまま使うのであれば、
Fruit.all.pluck(:name)
=> [ "りんご", "バナナ", "みかん", "メロン", "スイカ" ]
となります。
何が違うの?
pluck
のほうが、テーブルから特定のカラムのみを取得する分、メモリ使用率が抑えられ、同時に処理時間も節約できます。
実験してみる
試しに、300,000件のレコードが入ったテーブルを適当に用意しました。
ここについてはGimeiを使って適当に流し込んだだけなので割愛…
Company.count
=> 300000
そして、そのテーブルに対して、 map
と pluck
で name だけを取得した際のベンチマークを計測。
Benchmark.bm do |x|
x.report { Company.all.map(&:name) }
x.report { Company.all.pluck(:name) }
end
-----------
user system total real
1.876591 0.033513 1.910104 ( 2.718245)
0.117722 0.025000 0.142722 ( 0.545463)
map
のほうが pluck
の10倍以上かかってますね…
ならもう全部 pluck を使えば良くない?
じゃぁ全てにおいて pluck
を使えば良いかと言うと、そうではないのが面白いところです。
上記ではテーブルから直接データを取得しましたが、先にインスタンスを作っていた場合はどうなるでしょう。
# 先にインスタンスを作る
companies = Company.limit(1000)
# インスタンスに対して同様にmapとpluckを試す
Benchmark.bm do |x|
x.report { 1000.times { companies.map(&:name) } }
x.report { 1000.times { companies.pluck(:name) } }
end
-----------
user system total real
0.198063 0.000000 0.198063 ( 0.198079)
0.284302 0.000000 0.284302 ( 0.284310)
先ほどの結果とは一転、pluck
のほうが時間がかかっています。
これは何故かを調べてみると、 pluck
は毎度SQLを発行しているため、上記のコードの場合、SQLが1000回発行されている訳ですね。
一方で map
は、インスタンスに対してはSQLを発行しないため、処理が早くなっている、という仕掛けでした。
まとめ
- テーブルから直接値を取得するなら
pluck
を使う - インスタンス化したものに対して取得するのであれば、
pluck
よりmap
を使う
何も考えず、適当に pluck
ばかり使っていると、うっかりN+1問題を発生させてしまったりすることもあるかもしれないですね…!
コメント