根据数组排序对象列表(Rails)

时间:2017-01-01 21:25:00

标签: ruby-on-rails sorting activerecord rails-activerecord

如果来自表格的ID,我有一个数组:

b = [659, 658, 656, 645, 644, 657, 634, 643, 649, 650, 651, 636, 633, 607, 605, 604, 648, 647, 675, 674, 667, 499]

我用它来检索来自tat table的对象列表:

k = Photo.where(id:b)

我得到的是一个对象列表,它们的排序顺序与数组不同。

我该怎么做?

3 个答案:

答案 0 :(得分:2)

如果ID是唯一的

您可以使用index_byvalues_at

k = Photo.where(id: b).index_by(&:id).values_at(*b)

示例:

b = [5,3,1]
Country.where(id: b)
#=> [#<Country id: 1, name: "France">, #<Country id: 3, name: "Ukraine">,  #<Country id: 5, name: "Spain">]
Country.where(id: b).index_by(&:id)
#=> {1=>#<Country id: 1, name: "France">, 3=>#<Country id: 3, name: "Ukraine">, 5=>#<Country id: 5, name: "Spain">}
Country.where(id: b).index_by(&:id).values_at(*b)
#=> [#<Country id: 5, name: "Spain">, #<Country id: 3, name: "Ukraine">, #<Country id: 1, name: "France">]

如果b包含重复的ID

使用b = [5,3,3,1],第一个方法将输出:

#=> [#<Country id: 5, name: "Spain">, #<Country id: 3, name: "Ukraine">, #<Country id: 3, name: "Ukraine">, #<Country id: 1, name: "France">]

您可以使用index_byslice

k = Photo.where(id: b).index_by(&:id).slice(*b).values

使用:

b = [5,3,3,1]
Country.where(id: b)
#=> [#<Country id: 1, name: "France">, #<Country id: 3, name: "Ukraine">,  #<Country id: 5, name: "Spain">]
Country.where(id: b).index_by(&:id)
#=> {1=>#<Country id: 1, name: "France">, 3=>#<Country id: 3, name: "Ukraine">, 5=>#<Country id: 5, name: "Spain">}
Country.where(id: b).index_by(&:id).slice(*b)
#=> {5=>#<Country id: 5, name: "Spain">, 3=>#<Country id: 3, name: "Ukraine">, 1=>#<Country id: 1, name: "France">}
Country.where(id: b).index_by(&:id).slice(*b).values
#=> [#<Country id: 5, name: "Spain">, #<Country id: 3, name: "Ukraine">, #<Country id: 1, name: "France">]

当然,您也可以使用b.uniq

的第一种方法

答案 1 :(得分:1)

Photo.where(id: some_array)这样的ActiveRecord查询产生如下SQL:

select ... from photos where id in (...)

IN中元素的顺序与数据库返回行的顺序无关;通常,唯一确定行顺序的是ORDER BY子句。

您可以让数据库订购与b数组匹配的内容与CASE:

order by case id when 659 then 0 when 658 then 1 ... end

并且很容易在Ruby中构建:

Photo.where(id: b)
     .order(%Q{case id #{b.map.each_with_index { |id, i| "when #{id} then #{i}" }.join(' ')} end })

我认为并不是非常漂亮,但它让数据库完成工作(如果您将更多内容链接到此查询中,这可能很重要)并且您可以轻松地隐藏Photo类中的类方法中的丑陋: / p>

def self.for_ids_in_order(ids)
  where(id: ids).order(%Q{case id #{ids.map.each_with_index { |id, i| "when #{id} then #{i}" }.join(' ')} end })
end

然后说Photo.for_ids_in_order(b)

当然,这确实假设您知道b数组的来源,并且您知道它包含整数。如果您不确定,那么您可以拨打connection.quote来确保正确转义:

ids.map.each_with_index { |id, i| "when #{connection.quote(id)} then #{i}" }...

答案 2 :(得分:0)

b.map {| m |如果b数组中的ID是唯一的,则为Photo.find(m)} 。如果不 然后运行 b.uniq.map {| m | Photo.find(m)} 此代码的一个缺点是它对b数组中存在的每个元素运行查询。