2011-01-20 178 views
15

我有一個代表對象的哈希數組作爲對API調用的響應。我需要從一些哈希中提取數據,並且一個特定的鍵用作哈希對象的ID。我想將數組轉換爲一個散列,其中的鍵爲ids,值爲該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' }, 
    # .. 
} 

有兩種方法我能想到這樣做的。

  1. 地圖數據到ideal_response(下同)
  2. 使用api_response.find { |x| x[:id] == i }每個我需要訪問記錄。
  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流動。這就是爲什麼Facets提出Enumerable#mash

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

這一基本抽象(轉換可枚舉到哈希)被要求早就被列入紅寶石,唉,without luck

Ruby> = 2。1

[更新]仍然沒有愛Enumerable#mash,但現在我們有Array#to_h。不理想 - 因爲我們需要比沒有中間陣列 - 但更好:

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

喜歡的東西:

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

它使用可枚舉的group_by,這適用於集合,返回比賽爲任何你想要的鍵值。因爲它希望找到匹配鍵值命中的多次出現,所以它將它們附加到數組,所以最終得到散列數組的散列。如果需要,可以剝離內部數組,但如果兩個哈希ID相沖突,則可能會覆蓋內容。 group_by避免與內部陣列。

訪問特定的元素很簡單:

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

告訴你在這個問題結束的方式是這樣做的另一個有效途徑。兩者都相當快速和優雅。

+0

這比我在性能和整潔方面的映射要好,但通過data [id] [0]或data [id] .first訪問記錄有點麻煩。不過,我在這一點上挑剔。讓我們看看是否有其他人關心在戒指中戴帽子。 – coreyward 2011-01-20 23:50:39

0

爲此,我很可能只是去:

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

不是超級靚與多個括號中的塊,但它只需要api_response的一次迭代就能完成這個技巧。