有没有办法检查数组中的哈希是否包含ruby中类似的键值对?

例如,我有

array = [ {name: 'robert', nationality: 'asian', age: 10}, {name: 'robert', nationality: 'asian', age: 5}, {name: 'sira', nationality: 'african', age: 15} ] 

我希望得到结果

 array = [ {name: 'robert', nationality: 'asian', age: 15}, {name: 'sira', nationality: 'african', age: 15} ] 

因为有2个罗伯特具有相同的国籍。

任何帮助将非常感激。

我试过Array.uniq! {|e| e[:name] && e[:nationality] } Array.uniq! {|e| e[:name] && e[:nationality] } Array.uniq! {|e| e[:name] && e[:nationality] }但我想在两个哈希值中添加两个数字,即10 + 5

PS:数组可以有n个哈希值。

 array.each_with_object(Hash.new(0)) { |g,h| h[[g[:name], g[:nationality]]] += g[:age] }. map { |(name, nationality),age| { name:name, nationality:nationality, age:age } } [{ :name=>"robert", :nationality=>"asian", :age=>15 }, { :name=>"sira", :nationality=>"african", :age=>15 }] 

这两个步骤如下。

 a = array.each_with_object(Hash.new(0)) { |g,h| h[[g[:name], g[:nationality]]] += g[:age] } #=> { ["robert", "asian"]=>15, ["sira", "african"]=>15 } 

这使用类方法Hash :: new来创建一个默认值为零的哈希(由块变量h )。 一旦这个哈希heen获得它,构造所需的哈希是一件简单的事情:

 a.map { |(name, nationality),age| { name:name, nationality:nationality, age:age } } 

我会从这样的事情开始:

 array = [ { name: 'robert', nationality: 'asian', age: 10 }, { name: 'robert', nationality: 'asian', age: 5 }, { name: 'sira', nationality: 'african', age: 15 } ] array.group_by { |e| e.values_at(:name, :nationality) } .map { |_, vs| vs.first.merge(age: vs.sum { |v| v[:age] }) } #=> [ # { # :name => "robert", # :nationality => "asian", # :age => 15 # }, { # :name => "sira", # :nationality => "african", # :age => 15 # } # ] 

让我们来看看你想要完成什么并从那里开始。 您有一些对象的列表,如果它们具有相同的种族和名称,您希望将某些对象合并在一起。 所以我们有一个密钥,我们将合并。 让我们把它放在编程术语中。

 key = proc { |x| [x[:name], x[:nationality]] } 

我们已经定义了一个过程,它接受一个哈希并返回其“键”值。 如果此过程为两个哈希返回相同的值(根据eql? ),那么这两个哈希需要合并在一起。 现在,“合并”是什么意思? 你想把年龄加在一起,所以让我们写一个合并函数。

 merge = proc { |x, y| x.dup.tap { |x1| x1[:age] += y[:age] } } 

如果我们有两个值xy使得key[x]key[y]相同,我们想通过制作x的副本并将y的年龄添加到它来合并它们。 这正是这个程序的作用。 现在我们有了构建块,我们可以编写算法。

在使用我们编写的关键过程合并之后,我们想在最后生成一个数组。 幸运的是,Ruby有一个名为each_with_object的便捷函数,它将为我们做一些非常好的事情。 方法each_with_object将为数组的每个元素执行其块,传递预定值作为另一个参数。 这将在这里派上用场。

 result = array.each_with_object({}) do |x, hsh| # ... end.values 

由于我们使用键和值来进行合并,因此最有效的方法是使用哈希。 因此,我们传入一个空哈希作为额外的对象,我们将修改它以累积合并结果。 最后,我们不再关心键了,所以我们编写.values来获取对象本身。 现在为最后的作品。

 if hsh.include? key[x] hsh[ key[x] ] = merge.call hsh[ key[x] ], x else hsh[ key[x] ] = x end 

让我们打破这个。 如果散列已经包含key[x] ,这是我们正在查看的对象x的键,那么我们想要将x与当前在key[x]处的值合并。 这是我们将年龄加在一起的地方。 这种方法只有在merge函数是数学家称为半群的情况下才有效 ,这是一种说明操作是关联的奇特方式。 你不需要太担心; 加法是半群的一个很好的例子,所以它在这里工作。

无论如何,如果哈希中不存在密钥,我们希望将当前值放在密钥位置的哈希中。 返回合并产生的哈希值,然后我们可以从中获取值以获得您想要的结果。

 key = proc { |x| [x[:name], x[:nationality]] } merge = proc { |x, y| x.dup.tap { |x1| x1[:age] += y[:age] } } result = array.each_with_object({}) do |x, hsh| if hsh.include? key[x] hsh[ key[x] ] = merge.call hsh[ key[x] ], x else hsh[ key[x] ] = x end end.values 

现在,我的复杂性理论有点生疏,但如果Ruby有效地实现了它的哈希类型(我相当确定它会这样做),那么这个合并算法就是O(n) ,这意味着它需要花费一定的线性时间。完成,给出问题大小作为输入。