2011-01-20 13 views
15

APIコールへの応答としてオブジェクトを表すハッシュの配列があります。いくつかのハッシュからデータを取り出す必要があり、ある特定のキーがハッシュオブジェクトのIDとして機能します。私はIDとしてキーを持つハッシュとそのIDを持つ元のハッシュとしての値に配列を変換したいと思います。ハッシュの配列をハッシュの属性にインデックスされたハッシュハッシュに変換します。

api_response = [ 
    { :id => 1, :foo => 'bar' }, 
    { :id => 2, :foo => 'another bar' }, 
    # .. 
] 

ideal_response = { 
    1 => { :id => 1, :foo => 'bar' }, 
    2 => { :id => 2, :foo => 'another bar' }, 
    # .. 
} 

が、私はこれをやって考えることができ、2つの方法があります

は、ここで私が話してんですよ。

  1. は、私がアクセスする必要のあるレコードごとに使用api_response.find { |x| x[:id] == i }
  2. (下)ideal_responseにデータをマップします。
  3. 私が気付いていない方法は、おそらくmapを使ってハッシュを構築する方法です。マッピングの

私の方法:

keys = data.map { |x| x[:id] } 
mapped = Hash[*keys.zip(data).flatten] 

私は助けるが、これをするよりパフォーマンス、整然と方法があるように感じることができません。オプション2は、アクセスする必要があるレコードの数が非常に少ない場合に非常に効率的です。ここではマッピングが優れていますが、レスポンスには多くのレコードがある場合には分断され始めます。ありがたいことに、50-100以上のレコードがあるとは思わないので、マッピングで十分です。

Rubyでこれを行うには、よりスマートで、より洗練された、より効果的な方法がありますか?以下のような

答えて

18

ルビー< = 2.0

Hash[api_response.map { |r| [r[:id], r] }] 
# {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}} 

はしかし、Hash::[]はかなり醜いですと通常の左から右へのOOPの流れを壊します。 FacetsEnumerable#mashを提案した理由です:

require 'facets' 
api_response.mash { |r| [r[:id], r] } 
# {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}} 

この基本的な抽象化は、(ハッシュにenumerablesを変換)、without luckは残念ながら、ずっと前にルビーに含まれるように頼まれました。

ルビー> = 2。1

[UPDATE] Enumerable#mashためまだ愛が、今、私たちはArray#to_hを持っています。我々は中間array-を必要とするが、何もないよりはましではない理想的な-because:このため

# ruby 2.1 
api_response.map { |r| [r[:id], r] }.to_h 
0

何か:それは可算のgroup_byを使用しています

ideal_response = api_response.group_by{|i| i[:id]} 
#=> {1=>[{:id=>1, :foo=>"bar"}], 2=>[{:id=>2, :foo=>"another bar"}]} 

、あなたが好きなキー値のために試合を返す、コレクションに取り組んでいます。一致するキー値のヒットが複数見つかると、配列にそれらが追加されるため、ハッシュ配列のハッシュになります。必要に応じて内部配列を取り除くことができますが、ハッシュIDの2つが衝突した場合にコンテンツを上書きする危険性があります。 group_byは、それを内部配列で回避します。特定の要素へのアクセス

は簡単です:あなたが質問の最後に表示さ

ideal_response[1][0]  #=> {:id=>1, :foo=>"bar"} 
ideal_response[1][0][:foo] #=> "bar" 

方法は、それを行うための別の有効な方法です。どちらも合理的に高速でエレガントです。

+0

これはパフォーマンスと整頓の点で私が持っていたマッピングより優れていますが、 'data [id] [0]'や 'data [id] .first'を介してレコードにアクセスするのはやや面倒です。私はこの時点でニックピッキングしています。他の誰かがリングに帽子を投げ捨てるかどうか見てみましょう。 – coreyward

+0

これは、重複したキーを実行する可能性があるためです。 'group_by'は未知のデータで安全のために設計された汎用ルーチンです。重複や重大なコリジョンやデータの損失に遭遇することは決してないなど、アプリケーション固有の知識を持つことで、データ構造を1レベル単純化できます。 –

0

私はおそらくちょうどいいと思う:

ideal_response = api_response.each_with_object(Hash.new) { |o, h| h[o[:id]] = o } 

ないブロック内に複数のブラケット付きの超かわいいけど、それapi_responseの単回の反復でトリックを行います。

関連する問題