如何按多个键排序,其中一些按降序排序

时间:2013-03-15 16:07:03

标签: ruby

我有一系列哈希:

a = [
  { :id => 10, :name => 'bush' },
  { :id => 2, :name => 'sugar' },
  { :id => 10, :name => 'mountain' },
  { :id => 10, :name => 'bug' },
  { :id => 8, :name => 'sugar' }
]

我想首先按数字上升的数字对数组进行排序,然后按名称按字母顺序降序,以便最终结果为:

a = [
  { :id => 2, :name => 'sugar' },
  { :id => 8, :name => 'sugar' },
  { :id => 10, :name => 'mountain' },
  { :id => 10, :name => 'bush' },
  { :id => 10, :name => 'bug' }
]

我如何实现这一目标?

5 个答案:

答案 0 :(得分:4)

根据您对问题所做的修改,要进行传统排序,您可以使用

a.sort { |a, b| [a[:id], a[:name]] <=> [b[:id], b[:name]] }
=> [
  {:id=>2, :name=>"sugar"},
  {:id=>8, :name=>"sugar"},
  {:id=>10, :name=>"bug"},
  {:id=>10, :name=>"bush"},
  {:id=>10, :name=>"mountain"}
]  

您可以通过交换条件检查来切换排序顺序。

a.sort { |a, b| [a[:id], b[:name]] <=> [b[:id], a[:name]] }
=> [
  {:id=>2, :name=>"sugar"},
  {:id=>8, :name=>"sugar"},
  {:id=>10, :name=>"mountain"},
  {:id=>10, :name=>"bush"},
  {:id=>10, :name=>"bug"}
]

答案 1 :(得分:1)

a.sort {| a,b | (a [:id]!= b [:id])? a [:id]&lt; =&gt; b [:id]:b [:name]&lt; =&gt; a [:name]}

>> a
=> [{:id=>10, :name=>"bush"}, {:id=>2, :name=>"sugar"}, {:id=>10, :name=>"mountain"}, {:id=>10, :name=>"bug"}, {:id=>8, :name=>"sugar"}]
>> a.sort {|a,b| (a[:id] != b[:id]) ? a[:id] <=> b[:id] : b[:name] <=> a[:name] }
=> [{:id=>2, :name=>"sugar"}, {:id=>8, :name=>"sugar"}, {:id=>10, :name=>"mountain"}, {:id=>10, :name=>"bush"}, {:id=>10, :name=>"bug"}]
>>

答案 2 :(得分:1)

我认为@DanReedy的答案非常清晰,但如果你将它应用于大型列表可能会产生性能问题,因为它会产生很多(O(N logN)的小型中间数组和重复的哈希查找。{{一旦在O(N logN)比较中重复使用O(N),可以帮助制作排序键。为了完成二级密钥的降序排序,我们需要一种方法来反转比较方法的排序。

sort_by

答案 3 :(得分:1)

只是为了好玩,这是一种多年来一直存在的模式:

class SortByInverter < Struct.new(:value)
  def <=>(other)
    other.value <=> value
  end
end

class Object
  def desc
    SortByInverter.new(self)
  end
end

现在让我们使用它:

hs.sort_by { |h| [h[:id], h[:name].desc] }

当然我们可以直接使用容器类(更详细,但没有Object的可怕扩展名):

hs.sort_by { |h| [h[:id], SortByInverter.new(h[:name])] }

答案 4 :(得分:0)

你可以尝试类似的东西

a.sort do |a1, a2|
  comparison = a1[:id] <=> a2[:id]
  (comparison != 0) ? comparison : a1[:name] <=> a2[:name]
end