2011-02-07 67 views
7

我正在发送需要按其值排序的嵌套散列。例如:按照它们的值对嵌套散列中的项目进行排序

@foo = {"a"=>{"z"=>5, "y"=>3, "x"=>88}, "b"=>{"a"=>2, "d"=>-5}} 

当运行以下:

@foo["a"].sort{|a,b| a[1]<=>b[1]} 

我得到:

[["y", 3], ["z", 5], ["x", 88]] 

这是伟大的,它正是我想要的。问题是我并不总是知道发送给我的所有密钥是什么,所以我需要某种循环。我试图做到以下几点:

@foo.each do |e| 
    e.sort{|a,b| a[1]<=>b[1]} 
end 

这对我来说是有道理的,因为如果我手动调用@ foo.first [0]我得到

"a" 

和@ foo.first [1]返回

{"z"=>5, "y"=>3, "x"=>8} 

但由于某种原因,这是不正确排序(如所有)。我认为这是因为每个对整个哈希对象进行排序而不是对“a”的值进行排序。如何在不知道关键是什么的情况下访问嵌套哈希的值?

回答

6

你可能要循环哈希是这样的:

 
@foo.each do |key, value| 
    @foo[key] = value.sort{ |a,b| a[1]<=>b[1] } 
end 
+0

1)从概念上讲,最好使用Enumerable#sort_by来代替排序,请检查Phrogz的答案。 2)这使得就地操作,创建新的对象(功能方法)使代码更容易遵循。 – tokland 2011-02-07 20:18:27

+0

@tokland - 所有要点,但我的目标是遵循OP代码,同时仍然解决实际问题。虽然创建新对象可能更容易遵循,但如果要将其分配回原始,则不会创建临时变量。 – 2011-02-07 20:33:35

4
@foo = {"a"=>{"z"=>5, "y"=>3, "x"=>88}, "b"=>{"a"=>2, "d"=>-5}} 
@bar = Hash[ @foo.map{ |key,values| [ key, values.sort_by(&:last) ] } ] 

或者,通过-棘手少路径:

@bar = {} 
@foo.each do |key,values| 
    @bar[key] = values.sort_by{ |key,value| value } 
end 

在这两种情况下@bar原来是:

p @bar 
#=> { 
#=> "a"=>[["y", 3], ["z", 5], ["x", 88]], 
#=> "b"=>[["d", -5], ["a", 2]] 
#=> } 
0

在你的例子中e是一个包含[key,value]对的临时数组。在这种情况下,字符键和嵌套散列。所以e.sort{|a,b|...}会尝试将字符与哈希进行比较,并失败并出现运行时错误。我想你可能打算输入e[1].sort{...}。但即使这样也不会正常工作,因为您不会将已排序的散列存储在任何地方:@foo.each会返回原始的@foo并使其保持不变。

更好的解决方案是一个由@Pan Thomakos建议:

@foo.each do |key, value| 
    @foo[key] = value.sort{ |a,b| a[1]<=>b[1] } 
end 
1

我的同事想出了一个稍微灵活的解决方案,这将递归排序任意深度的数组:

def deep_sort_by(&block) 
    Hash[self.map do |key, value| 
    [if key.respond_to? :deep_sort_by 
     key.deep_sort_by(&block) 
    else 
     key 
    end, 

    if value.respond_to? :deep_sort_by 
     value.deep_sort_by(&block) 
    else 
     value 
    end] 

    end.sort_by(&block)] 
end 

你可以把它注射到所有的哈希值,然后就这样称呼它:

myMap.deep_sort_by { |obj| obj } 

的代码对于数组是相似的。我们published it as a gem供他人使用,请参阅blog post了解更多详情。

声明:我为这家公司工作。

相关问题