2015-10-24 130 views
0

阵列在Ruby中的一个阵列的哈希值,是通过有得分下降到阵列的这个哈希排序简短而亲切的方式:排序由红宝石

scored = {:id=>[1, 2, 3], :score=>[8.3, 5, 10]} 

所以它看起来是这样的?:

scored = {:id=>[3, 1, 2], :score=>[10, 8.3, 5]} 

我找不到一个例子,我可以在这样的散列内排序数组?我可以用一些令人讨厌的代码做到这一点,但我觉得应该有一个或两个班轮呢?

回答

3

你可以使用sort_by

scored = {:id=>[1, 2, 3], :score=>[8.3, 5, 10]} 

scored.tap do |s| 
    s[:id] = s[:id].sort_by.with_index{ |a, i| -s[:score][i] } 
    s[:score] = s[:score].sort_by{ |a| -a } 
end 
#=> {:id=>[3, 1, 2], :score=>[10, 8.3, 5]} 
+0

去与这一个,因为我觉得这是稍微沟通。谢谢你。 – joshweir

+0

同意可读性。在所有情况下进行两次'sort_by'调用的开销都可以忽略不计,并且也不需要分配额外的数组,如我的答案。唯一的(高度理论上的)并发问题是,如果你在排序调用之间改变了一个分数(当然这绝不会发生真实生活),那么你就被搞砸了。 :-)这可能是最好的答案。 – Drenmi

+0

@Drenmi,我不同意。当我们可以分类一次时,我们不应该排序两次。如果值数组足够大,那么一次排序的方法将显着提高效率。而且,排序两次,即imo会对可读性产生不利影响。对不起,fl00r,但我不认为这个答案符合你通常的高标准。 –

1

以下是一种可能的解决方案。它有一个中间步骤,它利用scores对象的压缩版本,但会产生正确的输出:

s = scored.values.inject(&:zip).sort_by(&:last).reverse 
#=> [[3, 10], [1, 8.3], [2, 5]] 

result = { id: s.map(&:first), score: s.map(&:last) } 
#=> { :id => [3, 1, 2], :score => [10, 8.3, 5] } 
2
order = scored[:score].each_with_index.sort_by(&:first).map(&:last).reverse 
    #=> [2,0,1] 
scored.update(scored) { |_,a| a.values_at *order } 
    #=> {:id=>[3, 1, 2], :score=>[10, 8.3, 5]} 

如果scored是不被突变,用merge代替update

几点:

  • 计算order很容易让读者了解发生了什么事情。
  • 第二行使用Hash#merge的形式,它使用一个块来确定存在于两个哈希中的键的值(这里是所有键)。这是修改散列值的一种方便的方法(通常),部分原因是返回了新的散列值。
  • 我按顺序排序然后颠倒过来,而不是按否定值排序,以使该方法更具破坏性。 (也就是说,作为值的数组元素可以来自实现<=>的任何类)。
  • 使用Ruby 2.2+,按降序排列数组arr的另一种方法是使用Enumerable#max_byarr.max_by(arr.size).to_a

第一行可以替换为:

arr = scored[:score] 
order = arr.each_index.sort_by { |i| arr[i] }.reverse 
    #=> [2,0,1] 
+0

我喜欢这个。特别是与命令的诡计。 – fl00r