LoginSignup
41

More than 5 years have passed since last update.

RailsのActive Recordで指定した順序通りに取得したい

Last updated at Posted at 2014-07-15

概要

Redis等のKVSでランキングロジックを持っていて、
その詳細データはMySQL等のRDSで持つことが少なからずあると思う。
kvsで取得した複数のidをactive_recordで指定した順序通りに取得したい。

ただしそのままにやると…

pry(main)> array = [1, 6, 3, 10, 7]
=> [1, 6, 3, 10, 7]

pry(main)> User.find(array).collect(&:id)
=> [1, 3, 6, 7, 10]

順序が変わって出力されてしまう。
それを解決する方法をまとめてみた。

内容

結論は

Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values

例はこんな感じ

pry(main)> array = [1, 6, 3, 10, 7]
=> [1, 6, 3, 10, 7]

pry(main)> User.find(array).index_by(&:id).slice(*array).values.collect(&:id)
=> [1, 6, 3, 10, 7]

簡単に解説していく

index_by(&:id)

# index_byを使うことでこの形式ではなく
[
  #<data1>, 
  #<data2>, 
  #<data3>, ...
]
# この形式になる
{ 
  :id => #<data1>, 
  :id => #<data2>, 
  :id => #<data3>, ... 
}
# 上記の例だと下記のようになる
{ 
   1 => #<user1>, 
   3 => #<user3>, 
   6 => #<user6>, ... 
}

slice(*array)

# sliceを使うと指定したkeyのhashを返す。
# 配列をそのまま渡してももちろんダメ
hash = {1 => "1", 2 => "2", 4 => "4"}.slice([4,1])
=> {}

# 下記の用に引数展開で渡してあげる
hash = {1 => "1", 2 => "2", 4 => "4"}.slice(*[4,1])
=> {4=>"4", 1=>"1"}

# なので、これでも意味は同じ
hash = {1 => "1", 2 => "2", 4 => "4"}.slice(4,1)
=> {4=>"4", 1=>"1"}

values

# そしてそのhashの値だけをとってくる。
pry(main)> hash = {1 => "1", 2 => "2", 4 => "4"}.slice(4,1)
=> {4=>"4", 1=>"1"}

pry(main)> hash.values
=> ["4", "1"]

こんな感じでやれば指定した順序どおりに取得することが出来る。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41