如何在不改变相同值的位置的情况下按降序排序哈希表(Hash)(ruby语言)

3
hash = {"p1"=>56, "p2"=>56, "p3"=>0, "p4"=>56, "p5"=>56, "p6"=>64, "p7"=>0}

p Hash[hash.sort_by{|k,v| v}.reverse] # gives 
{"p6"=>64, "p5"=>56, "p4"=>56, "p2"=>56, "p1"=>56, "p3"=>0}

但我希望输出如下:
{"p6"=>64, "p1"=>56, "p2"=>56, "p4"=>56, "p5"=>56}

此外,最终哈希不需要具有零键。

不要费心对哈希进行排序,这样做没有任何优势。相反,检索键并对其进行排序以提供检索值的顺序,或将哈希转换为数组并对其进行排序。实际上,对哈希进行排序并不值得消耗 CPU 时间。 - the Tin Man
2个回答

2
p Hash[
  hash.reject { |_, v| v.zero? }.sort do |kv1, kv2| 
    (val = kv1.last <=> kv2.last).zero? ? kv2.first <=> kv1.first : val
  end.reverse]

#⇒ {"p6"=>64, "p1"=>56, "p2"=>56, "p4"=>56, "p5"=>56}

更新 根据@CarySwoveland的建议表达块变量,并删除多余的reverse

p Hash[
  hash.reject { |_, v| v.zero? }.sort do |(k1,v1), (k2,v2)| 
    (val = v2 <=> v1).zero? ? k1 <=> k2 : val
  end
]

#⇒ {"p6"=>64, "p1"=>56, "p2"=>56, "p4"=>56, "p5"=>56}

NB: 上述内容将对相同值的键进行排序,而下面Cary的回答则不会。


不错的解决方案。考虑表达块变量 |(k1,v1),(k2,v2)| - Cary Swoveland
@CarySwoveland 谢谢。我个人不喜欢块变量表达式,别问我为什么 :) 顺便说一下,交换 kvN 条件可以帮助摆脱后面的 reverse 调用。 - Aleksei Matiushkin
为什么?:-)。Hari,按照我建议的方式将块变量“消除歧义”,操作行如下:(v1==v2) ? k2.<=> k1 : v1。(我还稍微简化了一下。) - Cary Swoveland

2

另一种方式:

hash.reject { |_,v| v.zero? }
    .each_with_index
    .sort_by { |(_,v),i| [-v,i] }
    .map(&:first)
    .to_h
  #=> {"p6"=>64, "p1"=>56, "p2"=>56, "p4"=>56, "p5"=>56} 

对于 Ruby 2.0 版本之前的版本,请使用 Hash[arr] 替换 arr.to_h

具体步骤:

h = hash.reject { |_,v| v.zero? }
  #=> {"p1"=>56, "p2"=>56, "p4"=>56, "p5"=>56, "p6"=>64} 
e = h.each_with_index
  #=> #<Enumerator: {"p1"=>56, "p2"=>56, "p4"=>56, "p5"=>56,
  #                  "p6"=>64}:each_with_index> 
a = e.sort_by { |(_,v),i| [-v,i] }
  #=> [[["p6", 64], 4], [["p1", 56], 0], [["p2", 56], 1],
  #    [["p4", 56], 2], [["p5", 56], 3]]
b = a.map(&:first)
  #=> [["p6", 64], ["p1", 56], ["p2", 56], ["p4", 56], ["p5", 56]]
b.to_h
  #=> {"p6"=>64, "p1"=>56, "p2"=>56, "p4"=>56, "p5"=>56}

我们可以通过将枚举器 e 转换为数组来查看将传递到块中的值:

e.to_a
  #=> [[["p1", 56], 0], [["p2", 56], 1], [["p4", 56], 2],
  #    [["p5", 56], 3], [["p6", 64], 4]] 

编辑: 如果我误解了问题(请查看下面的评论),并且@mudasobwa的解释是正确的,那么我的解决方案可以修改如下:

hash.reject { |_,v| v.zero? }.sort_by { |k,v| [-v,k] }.to_h

这个无法在混乱的输入上工作:hash = {"p2"=>56, "p1"=>56, "p6"=>64, "p7"=>0}(注意 p2p1 的顺序)。事实上,涉及索引是不太实用的 :) - Aleksei Matiushkin
@mudasobwa,你的例子产生了结果{"p6"=>64, "p2"=>56, "p1"=>56},这与我对问题的解释一致:键值相等的键在哈希表中保持其顺序。我注意到你对问题的解释不同。 - Cary Swoveland
啊...我越想越担心,是你正确解释了问题,而不是我。 - Aleksei Matiushkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接