[英]Get unique properties from array of hashes in ruby
Given an array of hashes, I want to create a method that returns a hash where the keys are the unique values of the hashes in the array. 给定一个哈希数组,我想创建一个返回哈希值的方法,其中键是数组中哈希值的唯一值。
For example, I'd like to take 例如,我想拿
[
{foo: 'bar', baz: 'bang'},
{foo: 'rab', baz: 'bang'},
{foo: 'bizz', baz: 'buzz'}
]
and return 然后返回
{
foo: ['bar', 'rab', 'bizz'],
baz: ['bang', 'buzz']
}
I am currently accomplishing this using: 我目前正在使用以下方法完成此操作:
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
Is there a more elegant way of doing this? 有没有更优雅的方式做到这一点? Thanks! 谢谢!
Your current approach is already pretty good; 您当前的方法已经相当不错了; I don't see much room for improvement. 我认为没有太大的改进空间。 I would write it like this: 我会这样写:
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: []
. 通过为每个哈希值设置默认值 ,我消除了显式声明foo: [], bar: []
。 each_with_object
, I have eliminated the need to declare a local variable and explicitly return it at the end. 通过使用each_with_object
,我消除了声明局部变量并在最后显式返回它的需要。 Set
, there is no need to call uniq
on the final result. 通过使用Set
,无需对最终结果调用uniq
。 This requires less code, and is more performant. 这需要更少的代码,并且性能更高。 However, if you really want the final result to be a mapping to Array
s rather than Set
s, then you would need to call to_a
on each value at the end of the method. 但是,如果您确实希望最终结果是到Array
而不是Set
的映射,则需要在方法末尾的每个值上调用to_a
。 data_list
and data
. 我为data_list
和data
使用了不同的变量名。 Call these whatever you like, but it's typically considered bad practice to shadow outer variables. 随便您如何称呼它们,但是通常不建议使用外部变量。 Here are a couple of one-liners. 这是一对单线。 (I'm pretty sure @eiko was being facetious, but I'm proving him correct) (我很确定@eiko一直在嬉戏,但我证明他是正确的)
This one reads well and is easy to follow (caveat: requires Ruby 2.4+ for transform_values
): 这本书读起来很好,很容易理解(注意: transform_values
需要Ruby 2.4+):
array.flat_map(&:entries).group_by(&:first).transform_values{|v| v.map(&:last).uniq}
Here's another, using the block form of merge
to specify an alternate merge method, which in this case is combining the values into a uniq array: 这是另一种方法,使用merge
的块形式指定替代的合并方法,在这种情况下,该方法将值合并为uniq数组:
array.reduce{|h, el| h.merge(el){|k, old, new| ([old]+[new]).flatten.uniq}}
You already have a pretty good answer, but I felt golfy and so here is a shorter one: 您已经有了一个不错的答案,但是我感到高尔夫球了,所以这里的答案更短:
def the_combiner(a)
hash = {}
a.map(&:to_a).flatten(1).each do |k,v|
hash[k] ||= []
hash[k].push(v)
end
hash
end
Try this: 尝试这个:
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}
If, as in the example, all hashes have the same keys, you could do as follows. 如果像本例中那样,所有散列都具有相同的密钥,则可以执行以下操作。
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"]}
The steps are as follows. 步骤如下。
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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.