2011-02-07 5 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 

[0]私は

"a" 

を取得し、@ foo.first [1]

を返しfoo.first私は手動で@を呼び出す場合ので、私にはこれが理にかなって:私は、次の手順を実行してみました
{"z"=>5, "y"=>3, "x"=>8} 

しかし、何らかの理由で、これは適切にソートされていません(たとえば、まったく)。これは、それぞれが "a"の値ではなく、ハッシュオブジェクト全体でソートを呼び出すためだと仮定します。ネストされたハッシュの値にアクセスするには、それが重要なことを知らずにどうすればよいですか?

答えて

6

あなたは、このようなハッシュをループすることがあります:@barがあることが判明両方のケースで

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

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

1)ソートの代わりにEnumerable#sort_byを使用することは概念的には好都合ですが、Phrogzの答えを確認してください。 2)これはインプレイス・オペレーションを行い、新しいオブジェクトを作成する(機能的なアプローチ)ことで、コードを簡単にフォローできます。 – tokland

+0

@tokland - すべての素晴らしい点ですが、私の目標は実際の問題を解決しながらOPコードに従うことでした。新しいオブジェクトを作成する方が簡単ですが、仮の変数をオリジナルに戻したい場合は、それを作成しない方がきれいです。 –

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) ] } ] 

あるいは、あまりトリッキーな経路を介して:

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

+1しかし、私はsort_byの明示的なゲッターを書くだろう:values.sort_by {| k、v | v}。 Rubyが絶対に必要なEnumerable#to_hashを出荷する日が見えますか?私はファセットのEnumberable#マッシュを組み込むべきであると勧めることに飽き飽きしています。一口= @ bar = @ foo.to_hash {| key、values | [key、values.sort_by {| k、v | v}]} – tokland

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を参照してください。

免責事項:私はこの会社の仕事をしています。

関連する問題