从ruby中的哈希数组中获取独特的属性

时间:2017-06-22 17:02:09

标签: ruby-on-rails 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

有更优雅的方式吗?谢谢!

5 个答案:

答案 0 :(得分: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以遮蔽外部变量。

答案 1 :(得分:2)

这里有几个单行。 (我很确定@eiko很滑稽,但我证明他是正确的)

这个很好读,很容易理解(警告:transform_values需要Ruby 2.4+):

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

答案 2 :(得分:1)

你已经有了一个很好的答案,但我感觉很高兴,所以这里有一个较短的答案:

{{1}}

答案 3 :(得分: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}

答案 4 :(得分: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>