駆け出しにもわかる簡単な並び替え
はじめまして、1年を経過した駆け出しRailsエンジニアです。
Model.where()
で取得したリレーションを任意のID順で並び替えたいということがありました。
この記事はRails6向けとなっており、Rails7の方は最後の項目だけ読んで頂ければOKです
それでは早速、下記のような animals
テーブルを例として進めて行きます。
ID | 名前(必要な情報) | 年齢(不要な情報) |
---|---|---|
1 | taro | 2 |
2 | hanako | 7 |
3 | jiro | 4 |
まずはID順に並べる場合
animal_ids = [1, 3, 2]
Animal.where(id: animal_ids).order(:id).pluck(:name)
# SELECT "animals"."name" FROM "animals" WHERE "animals"."id" IN ($1, $2, $3) ORDER BY "animals"."id" ASC
# => ["taro", "hanako", "jiro"]
何も気にすることはありませんね。
それでは本題へ
指定したID順に並べたい
MySQLの場合は FIELD
が使えるそうです。
ORDER BY FIELD( id, '1', '3', '2');
私の環境はPostgreSQLの為、上記は見なかったことにします
PostgreSQLの場合は CASE
でやるようです。
CASE id WHEN '1' THEN 0 WHEN '3' THEN 1 WHEN '2' THEN 2 END
というのがSQLでの並び替え方法のようです。
Arel
を使用してSQLを書けばできるそうですが、、、 駆け出しには難しい
sort_by
を使った並び替え
今回はもっと簡単にRailsで sort_by
を使用して並び替えることにします。
animal_ids = [1, 3, 2]
Animal.where(id: animal_ids).sort_by { |animal| animal_ids.index(animal.id) }.pluck(:name)
# SELECT "animals".* FROM "animals" WHERE "animals"."id" IN ($1, $2, $3)
# => ["taro", "jiro", "hanako"]
SQLで取得したものをRailsで並び替えていますが、多くの場合はこれで十分ではないでしょうか
違いはSQL上で並び替えていないという点と ActiveRecord_Relation
から Array
に変わっている点です。
少し気になるのは SELECT "animals".*
となっており pluck(:name)
が効かず、全ての値を取得しています。
これは必要な値(name)とIDをSELECTしてあげましょう!
Animal.where(id: animal_ids).select(:id, :name).sort_by { |animal| animal_ids.index(animal.id) }.pluck(:name)
# SELECT "animals"."id", "animals"."name" FROM "animals" WHERE "animals"."id" IN ($1, $2, $3)
# => ["taro", "jiro", "hanako"]
良さそうですね
抽象化したscopeを作っておいても色々使えるかもしれません。
任意のカラムを好きな順に並べ替えることができます。
# @return [Array]
# @example
# ActiveRecord_Relation.in_order_of(:id, [1, 3, 2])
# => [Model(id: 1), Model(id: 3), Model(id: 2)]
scope :in_order_of, -> (column, values) {
sort_by { |x| values.index(x[column]) }
}
ん、in_order_of
って、なに?
Rails7になれば in_order_of
が使えます
はい、色々頑張りましたが、Rails7の方はSQL上でできちゃいます
ということで、今回の内容はRails6の方でRails7に上げるまで用として検討して頂ければと思います。
最後まで読んで頂きありがとうございました。