ハッシュの属性によってインデックス付けされたハッシュの配列をハッシュのハッシュに変換します。

概要

API 呼び出しへの応答としてオブジェクトを表すハッシュの配列を取得しました。いくつかのハッシュからデータを取得する必要があり、1 つの特定のキーがハッシュ オブジェクトの 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 つの方法が考えられます。

私のマッピング方法:

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

これをもっと効率的で整然とした方法があるような気がしてなりません。オプション 2 は、アクセスする必要があるレコードの数が最小限である場合に非常にパフォーマンスが高くなります。この場合、マッピングは優れていますが、応答に多数のレコードが含まれる場合には機能しなくなり始めます。ありがたいことに、レコードが 50 ~ 100 件を超えることはないと思われるため、マッピングで十分です。

Ruby でこれを行う、より賢く、整然とした、またはよりパフォーマンスの高い方法はあるでしょうか?

解決策

ルビー <= 2.0

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

ただし、Hash::[] は非常に醜く、通常の左から右への OOP フローを壊します。それが、Facets が Enumerable#mash を提案した理由です。

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

この基本的な抽象化 (列挙型をハッシュに変換する) は、ずっと前に Ruby に含めることを求められましたが、残念ながら運がありませんでした。

あなたのユースケースはアクティブサポートでカバーされていることに注意してください: Enumerable#index_by

ルビー >= 2.1

[更新] Enumerable#mash はまだ好きではありませんが、Array#to_h が使えるようになりました。中間配列が作成されますが、何もしないよりはマシです。

> object = api_response.map { |r| [r[:id], r] }.to_h