【Rails】メモリや実行時間を意識したpluckとmapの使い分け

Ruby

pluckmapを利用することで、Active Recordモデルから特定の要素を取り出せます。
今回はpluckmapの使い分けについて紹介します。

pluckについて

pluckはRailsのメソッドです。引数で指定したカラムのみをデータベースから直接取得します。
特定のカラムのみデータベースから取り出すためメモリを節約できるメリットがあります。

# pluckの引数(last_name)のみがSELECTに指定されている。
# last_name以外のカラムはSELECTされないのでメモリを節約できる

> User.all.pluck(:last_name)

  (1.0ms)  SELECT `users`.`last_name` FROM `users`
=> ["Cormier", "Reichert", "Hauck",...]

一方で、メソッド実行時に毎回SQLが発行されるというデメリットがあります。

mapについて

mapはRubyのメソッドです。ブロック内の処理を実行したレシーバを配列として返します。
mapはレシーバをメモリに読み込むためメモリを浪費するデメリットがあります。

# last_nameだけで十分なのにSELECTは全選択(*)になっている
# レシーバを加工するため、Userモデルが読み込まれることになる

> User.all.map(&:last_name)

  User Load (2.0ms)  SELECT `users`.* FROM `users`
=> ["Cormier", "Reichert", "Hauck",...]

pluckを利用したほうがよい場合

特定のカラムのみ利用する場合はメモリ節約の観点からpluckを利用したほうがよいです。

# こっちよりは↓
> User.all.map(&:last_name)

# こっちのほうがいい↓
> User.all.pluck(&:last_name)

mapを利用したほうがよい場合

インスタンス化されたActive Recordモデルから特定のカラムを取り出す場合はmapを利用したほうがよいです。
pluckではインスタンスからカラムを取得する場合もSQLが実行されるため、処理が遅くなってしまいます。一方mapの場合SQLは毎回実行されません。

> users = User.limit(20)

# pluckでは毎回SQLが実行されている
> 5.times { users.pluck(:last_name) }
   (0.7ms)  SELECT `users`.`last_name` FROM `users` LIMIT 20
   (0.4ms)  SELECT `users`.`last_name` FROM `users` LIMIT 20
   (0.5ms)  SELECT `users`.`last_name` FROM `users` LIMIT 20
   (0.5ms)  SELECT `users`.`last_name` FROM `users` LIMIT 20
   (0.4ms)  SELECT `users`.`last_name` FROM `users` LIMIT 20
=> 5

# mapでは毎回SQLが実行されない
> 5.times { users.map(&:last_name) }
  User Load (0.7ms)  SELECT `users`.* FROM `users` LIMIT 20
=> 5

こちらのコメントにもあるように、最近のpluckはSQLの再発行を防ぐ実装になっているため、場合によっては毎回SQLが実行されません。
ですが、インスタンスからカラムを取得する場合はmapを利用しておいたほうが無難です。

まとめ

pluckとmapの使い分け
  • 特定のカラムを取得するだけで十分な場合はpluckを利用する
  • インスタンス化されたActive Recordモデルからカラムを取得する場合はmapを利用する

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考