2017-06-22 48 views
1

給定一個散列數組,我想創建一個返回散列值的方法,其中的鍵是數組中散列的唯一值。從ruby數組中獲取獨特的屬性

例如,我想借此

[ 
    {foo: 'bar', baz: 'bang'}, 
    {foo: 'rab', baz: 'bang'}, 
    {foo: 'bizz', baz: 'buzz'} 
] 

,並返回

{ 
    foo: ['bar', 'rab', 'bizz'], 
    baz: ['bang', 'buzz'] 
} 

我目前完成這個使用:

def my_fantastic_method(data) 
    response_data = { foo: [], baz: []} 
    data.each { |data| 
    data.attributes.each { |key, value| 
     response_data[key.to_sym] << value 
    } 
    } 
    response_data.each { |key, value| response_data[key] = response_data[key].uniq } 
    response_data 
end 

是否有更優雅的方式這樣做?謝謝!

+0

唯一問心無愧你的代碼真正的問題是有一個硬編碼'foo'和'bar',並呼籲'它uniq'之前建立一個大陣的潛在性能成本。這些問題在你的應用程序中有重要意義嗎 –

+0

這是紅寶石!如果你的代碼長度超過一行,那*就是一個真正的問題。 – eiko

回答

6

你目前的做法已經很不錯了;我沒有看到很多的改善空間。我會寫這樣的:

def my_fantastic_method(data_list) 
    data_list.each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |data, result| 
    data.attributes.each do |key, value| 
     result[key.to_sym] << value 
    end 
    end 
end 
  • 通過在每個哈希值設置默認值,我已經消除了需要顯式聲明foo: [], bar: []
  • 通過使用each_with_object,我沒有必要聲明局部變量並在最後顯式返回它。
  • 通過使用Set,不需要在最終結果上調用uniq。這需要更少的代碼,並且性能更高。但是,如果您確實希望將最終結果映射到Array s而不是Set s,那麼您需要在該方法結尾的每個值上調用to_a
  • 我已使用data_listdata的不同變量名稱。儘可能地調用這些,但通常認爲bad practice會影響外部變量。
+0

我建議你爲任何不知道需要的讀者的好處添加'require'set''。 –

1

你已經有一個很好的答案,但我覺得golfy所以這裏是一個較短的一個:

def the_combiner(a) 
    hash = {} 
    a.map(&:to_a).flatten(1).each do |k,v| 
    hash[k] ||= [] 
    hash[k].push(v) 
    end 
    hash 
end 
2

這裏有一對夫婦的俏皮話。 (我敢肯定@eiko是在開玩笑,但我證明他是正確的)

這一個讀好,很容易跟着走(注意:需要紅寶石2.4及以上的爲transform_values):

array.flat_map(&:entries).group_by(&:first).transform_values{|v| v.map(&:last).uniq} 

下面是另一個,使用的merge塊形式指定備用合併方法,其在此情況下,這些值組合成的uniq數組:

array.reduce{|h, el| h.merge(el){|k, old, new| ([old]+[new]).flatten.uniq}} 
1

嘗試這種情況:

array.flat_map(&:entries) 
    .group_by(&:first) 
    .map{|k,v| {k => v.map(&:last)} } 

OR

a.inject({}) {|old_h, new_h| 
    new_h.each_pair {|k, v| 
    old_h.key?(k) ? old_h[k] << v : old_h[k]=[v]}; 
    old_h} 
0

如果,在本例中,所有的哈希值具有相同的鍵,你可以做如下。

arr = [{ foo: 'bar', baz: 'bang' }, 
     { foo: 'rab', baz: 'bang' }, 
     { foo: 'bizz', baz: 'buzz' }] 

keys = arr.first.keys 
keys.zip(arr.map { |h| h.values_at(*keys) }.transpose.map(&:uniq)).to_h 
    #=> {:foo=>["bar", "rab", "bizz"], :baz=>["bang", "buzz"]} 

步驟如下。

keys = arr.first.keys 
    #=> [:foo, :baz] 
a = arr.map { |h| h.values_at(*keys) } 
    #=> [["bar", "bang"], ["rab", "bang"], ["bizz", "buzz"]] 
b = a.transpose 
    #=> [["bar", "rab", "bizz"], ["bang", "bang", "buzz"]] 
c = b.map(&:uniq) 
    #=> [["bar", "rab", "bizz"], ["bang", "buzz"]] 
d = c.to_h 
    #=> <array of hashes shown above>