pluckメソッドがArrayのArrayではなくHashのArrayを返せるようにする
Bài đăng này đã không được cập nhật trong 10 năm
通好みのメソッドpluck
Railsでアプリケーションを組むと、ActiveRecordのインスタンス生成コストが勿体ないな、と感じることが多いと思います。
そんな時の強い味方がpluckメソッドで、愛用される方も多いと思います。
Rails4からは複数のカラムも指定できて、使い勝手が向上しました。 複数カラムを指定すると、値がArrayのArrayで返って来ます。
例を示すと、例えばこんなデータがあるとすると
- employees
| id | name | created_at | 
|---|---|---|
| 1 | Taro | 2014-08-01 01:00:05 | 
| 2 | Jiro | 2014-08-01 02:00:06 | 
| 3 | Saburo | 2014-08-01 03:00:07 | 
Emplyee.pluck :id, :name
=> [[1, "Taro"],
 [2, "Jiro"],
 [3, "Saburo"]]
のように返ります。 これはこれで良いのですが、この結果をJSONで返したい時等は、できればHashのArray、つまり
Emplyee.hash_pluck :id, :name
=> [{:id=>1, :name=>"Taro"},
 {:id=>2, :name=>"Jiro"},
 {:id=>3, :name=>"Saburo"}]
のように返って欲しいです。
これを実現できるように、pluckメソッドに手を入れました。
ついでに、tableの物理column名と論理column名が異なることもあるだろうと、key名も指定できるようにしています。
前提条件
Rails4.1で動作を確認しました。
できあがったものがこちら
コードの細かい説明は省きます。短時間で書いた、と言い訳を先にしておきます。
module ActiveRecord::Calculations
  def pluck_with_keys *column_names
    unless (options = column_names.pop).is_a? Hash
      return pluck_without_keys *(column_names << options)
    end
    _pluck = pluck_without_keys *column_names
    case keys = options[:keys]
    when TrueClass
      _pluck.map{|obj| Hash[*column_names.zip(obj).flatten]}
    when Array
      _pluck.map{|obj| Hash[*keys.zip(obj).flatten]}
    else
      _pluck
    end
  end
  alias_method_chain :pluck, :keys
end
これをRAILS_ROOT/lib/extensions/calculations.rb等に保存して、RAILS_ROOT/libをautoload_pathに追加した上でinitializer辺りでこのファイルをrequireします。
使ってみる
既存のシステムにも適用し易いように、元のpluckメソッドの挙動になるべく影響無いようにしています。
元々pluckは可変長引数を取りますが、最後の引数がオリジナルのpluckが取らないHashが来たら動きが変わります。
最後の引数にkeys: trueとすると、指定したcolumn名をkeyにしてHashが帰り、keys: [:employee_id, :employee_name]のようにkey名の配列を渡すと、そのように良い感じにしてくれます。
実際の動作はこのような感じです。
Employee.pluck :id, :name
=> [[1, "Taro"],
 [2, "Jiro"],
 [3, "Saburo"]]
Employee.pluck :id, :name, keys: true
=> [{:id=>1, :name=>"Taro"},
 {:id=>2, :name=>"Jiro"},
 {:id=>3, :name=>"Saburo"}]
Employee.pluck :id, :name, keys: [:employee_id, :employee_name]
=> [{:employee_id =>1, :employee_name=>"Taro"},
 {:employee_id =>2, :employee_name=>"Jiro"},
 {:employee_id =>3, :employee_name=>"Saburo"}]
key名が足りないと、あるものしか返しません。
Employee.pluck :id, :name, keys: [:employee_id]
=> [{:employee_id =>1},
 {:employee_id =>2},
 {:employee_id =>3}]
狙った通りに出来ました。
gemにしようかな。
All rights reserved
 
  
 