ruby使用某些键比较两个哈希数组

时间:2021-05-20 12:53:57

There's two arrays of hash and I want remove the 'common' elements from the two arrays, based on certain keys. For example:

有两个哈希数组,我想根据某些键从两个数组中删除'common'元素。例如:

array1 = [{a: '1', b:'2', c:'3'}, {a: '4', b: '5', c:'6'}]
array2 = [{a: '1', b:'2', c:'10'}, {a: '3', b: '5', c:'6'}]

and the criteria keys are a and b. So when I get the result of something like

标准键是a和b。所以当我得到类似的结果时

array1-array2 (don't have to overwrite '-' if there's better approach)

it will expect to get [{a: '4', b: '5', c:'6'}] sine we were using a and b as the comparing criteria. It will wipe the second element out since the value for a is different for array1.last and array2.last.

它会期望得到[{a:'4',b:'5',c:'6'}]正弦我们使用a和b作为比较标准。它将擦除第二个元素,因为array1.last和array2.last的a值不同。

2 个解决方案

#1


8  

As I understand, you are given two arrays of hashes and a set of keys. You want to reject all elements (hashes) of the first array whose values match the values of any element (hash) of the second array, for all specified keys. You can do that as follows.

据我所知,您将获得两个哈希数组和一组键。对于所有指定的键,您希望拒绝第一个数组的所有元素(哈希),其值与第二个数组的任何元素(哈希值)匹配。你可以这样做。

Code

require 'set'

def reject_partial_dups(array1, array2, keys)
  set2 = array2.each_with_object(Set.new) do |h,s|
     s << h.values_at(*keys) if (keys-h.keys).empty? 
  end
  array1.reject do |h|
    (keys-h.keys).empty? && set2.include?(h.values_at(*keys))
  end
end

The line:

(keys-h.keys).empty? && set2.include?(h.values_at(*keys))

can be simplified to:

可以简化为:

set2.include?(h.values_at(*keys))

if none of the values of keys in the elements (hashes) of array1 are nil. I created a set (rather than an array) from array2 in order to speed the lookup of h.values_at(*keys) in that line.

如果array1的元素(散列)中的键值都不是nil。我从array2创建了一个set(而不是一个数组),以加快该行中h.values_at(* keys)的查找速度。

Example

keys = [:a, :b]
array1 = [{a: '1', b:'2', c:'3'}, {a: '4', b: '5', c:'6'}, {a: 1, c: 4}]
array2 = [{a: '1', b:'2', c:'10'}, {a: '3', b: '5', c:'6'}]
reject_partial_dups(array1, array2, keys)
  #=> [{:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}] 

Explanation

First create set2

首先创建set2

e0 = array2.each_with_object(Set.new)
  #=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"10"}, {:a=>"3", :b=>"5", :c=>"6"}]
  #     #:each_with_object(#<Set: {}>)> 

Pass the first element of e0 and perform the block calculation.

传递e0的第一个元素并执行块计算。

h,s = e0.next
  #=> [{:a=>"1", :b=>"2", :c=>"10"}, #<Set: {}>]
h #=> {:a=>"1", :b=>"2", :c=>"10"} 
s #=> #<Set: {}> 
(keys-h.keys).empty?
  #=> ([:a,:b]-[:a,:b,:c]).empty? => [].empty? => true

so compute:

s << h.values_at(*keys)
  #=> s << {:a=>"1", :b=>"2", :c=>"10"}.values_at(*[:a,:b] }
  #=> s << ["1","2"] => #<Set: {["1", "2"]}> 

Pass the second (last) element of e0 to the block:

将e0的第二个(最后一个)元素传递给块:

h,s = e0.next
  #=> [{:a=>"3", :b=>"5", :c=>"6"}, #<Set: {["1", "2"]}>] 
(keys-h.keys).empty?
  #=> true

so compute:

s << h.values_at(*keys)
  #=> #<Set: {["1", "2"], ["3", "5"]}> 

set2
  #=> #<Set: {["1", "2"], ["3", "5"]}> 

Reject elements from array1

拒绝来自array1的元素

We now iterate through array1, rejecting elements for which the block evaluates to true.

我们现在遍历array1,拒绝块评估为true的元素。

e1 = array1.reject
  #=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"3"},
  #                  {:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}]:reject> 

The first element of e1 is passed to the block:

e1的第一个元素传递给块:

h = e1.next
  #=> {:a=>"1", :b=>"2", :c=>"3"} 
a = (keys-h.keys).empty?
  #=> ([:a,:b]-[:a,:b,:c]).empty? => true
b = set2.include?(h.values_at(*keys))
  #=> set2.include?(["1","2"] => true
a && b
  #=> true

so the first element of e1 is rejected. Next:

所以e1的第一个元素被拒绝了。下一个:

 h = e1.next
   #=> {:a=>"4", :b=>"5", :c=>"6"} 
 a = (keys-h.keys).empty?
   #=> true 
 b = set2.include?(h.values_at(*keys))
   #=> set2.include?(["4","5"] => false
 a && b
   #=> false

so the second element of e1 is not rejected. Lastly:

所以e1的第二个元素不被拒绝。最后:

h = e1.next
  #=> {:a=>1, :c=>4} 
a = (keys-h.keys).empty?
  #=> ([:a,:c]-[:a,:b]).empty? => [:c].empty? => false

so return true (meaning the last element of e1 is not rejected), as there is no need to compute:

所以返回true(意味着e1的最后一个元素不被拒绝),因为不需要计算:

 b = set2.include?(h.values_at(*keys))

#2


1  

So you really should try this out yourself because I am basically solving it for you.

所以你真的应该自己尝试一下,因为我基本上是为你解决的。

The general approach would be:

一般方法是:

  1. For every time in array1
  2. 对于array1中的每一次

  3. Check to see the same value in array2 has any keys and values with the same value
  4. 检查以查看array2中的相同值是否具有相同值的任何键和值

  5. If they do then, delete it
  6. 如果他们这样做,删除它

You would probably end up with something like array1.each_with_index { |h, i| h.delete_if {|k,v| array2[i].has_key?(k) && array2[i][k] == v } }

你可能会得到类似array1.each_with_index {| h,i |的东西h.delete_if {| k,v | array2 [i] .has_key?(k)&& array2 [i] [k] == v}}

#1


8  

As I understand, you are given two arrays of hashes and a set of keys. You want to reject all elements (hashes) of the first array whose values match the values of any element (hash) of the second array, for all specified keys. You can do that as follows.

据我所知,您将获得两个哈希数组和一组键。对于所有指定的键,您希望拒绝第一个数组的所有元素(哈希),其值与第二个数组的任何元素(哈希值)匹配。你可以这样做。

Code

require 'set'

def reject_partial_dups(array1, array2, keys)
  set2 = array2.each_with_object(Set.new) do |h,s|
     s << h.values_at(*keys) if (keys-h.keys).empty? 
  end
  array1.reject do |h|
    (keys-h.keys).empty? && set2.include?(h.values_at(*keys))
  end
end

The line:

(keys-h.keys).empty? && set2.include?(h.values_at(*keys))

can be simplified to:

可以简化为:

set2.include?(h.values_at(*keys))

if none of the values of keys in the elements (hashes) of array1 are nil. I created a set (rather than an array) from array2 in order to speed the lookup of h.values_at(*keys) in that line.

如果array1的元素(散列)中的键值都不是nil。我从array2创建了一个set(而不是一个数组),以加快该行中h.values_at(* keys)的查找速度。

Example

keys = [:a, :b]
array1 = [{a: '1', b:'2', c:'3'}, {a: '4', b: '5', c:'6'}, {a: 1, c: 4}]
array2 = [{a: '1', b:'2', c:'10'}, {a: '3', b: '5', c:'6'}]
reject_partial_dups(array1, array2, keys)
  #=> [{:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}] 

Explanation

First create set2

首先创建set2

e0 = array2.each_with_object(Set.new)
  #=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"10"}, {:a=>"3", :b=>"5", :c=>"6"}]
  #     #:each_with_object(#<Set: {}>)> 

Pass the first element of e0 and perform the block calculation.

传递e0的第一个元素并执行块计算。

h,s = e0.next
  #=> [{:a=>"1", :b=>"2", :c=>"10"}, #<Set: {}>]
h #=> {:a=>"1", :b=>"2", :c=>"10"} 
s #=> #<Set: {}> 
(keys-h.keys).empty?
  #=> ([:a,:b]-[:a,:b,:c]).empty? => [].empty? => true

so compute:

s << h.values_at(*keys)
  #=> s << {:a=>"1", :b=>"2", :c=>"10"}.values_at(*[:a,:b] }
  #=> s << ["1","2"] => #<Set: {["1", "2"]}> 

Pass the second (last) element of e0 to the block:

将e0的第二个(最后一个)元素传递给块:

h,s = e0.next
  #=> [{:a=>"3", :b=>"5", :c=>"6"}, #<Set: {["1", "2"]}>] 
(keys-h.keys).empty?
  #=> true

so compute:

s << h.values_at(*keys)
  #=> #<Set: {["1", "2"], ["3", "5"]}> 

set2
  #=> #<Set: {["1", "2"], ["3", "5"]}> 

Reject elements from array1

拒绝来自array1的元素

We now iterate through array1, rejecting elements for which the block evaluates to true.

我们现在遍历array1,拒绝块评估为true的元素。

e1 = array1.reject
  #=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"3"},
  #                  {:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}]:reject> 

The first element of e1 is passed to the block:

e1的第一个元素传递给块:

h = e1.next
  #=> {:a=>"1", :b=>"2", :c=>"3"} 
a = (keys-h.keys).empty?
  #=> ([:a,:b]-[:a,:b,:c]).empty? => true
b = set2.include?(h.values_at(*keys))
  #=> set2.include?(["1","2"] => true
a && b
  #=> true

so the first element of e1 is rejected. Next:

所以e1的第一个元素被拒绝了。下一个:

 h = e1.next
   #=> {:a=>"4", :b=>"5", :c=>"6"} 
 a = (keys-h.keys).empty?
   #=> true 
 b = set2.include?(h.values_at(*keys))
   #=> set2.include?(["4","5"] => false
 a && b
   #=> false

so the second element of e1 is not rejected. Lastly:

所以e1的第二个元素不被拒绝。最后:

h = e1.next
  #=> {:a=>1, :c=>4} 
a = (keys-h.keys).empty?
  #=> ([:a,:c]-[:a,:b]).empty? => [:c].empty? => false

so return true (meaning the last element of e1 is not rejected), as there is no need to compute:

所以返回true(意味着e1的最后一个元素不被拒绝),因为不需要计算:

 b = set2.include?(h.values_at(*keys))

#2


1  

So you really should try this out yourself because I am basically solving it for you.

所以你真的应该自己尝试一下,因为我基本上是为你解决的。

The general approach would be:

一般方法是:

  1. For every time in array1
  2. 对于array1中的每一次

  3. Check to see the same value in array2 has any keys and values with the same value
  4. 检查以查看array2中的相同值是否具有相同值的任何键和值

  5. If they do then, delete it
  6. 如果他们这样做,删除它

You would probably end up with something like array1.each_with_index { |h, i| h.delete_if {|k,v| array2[i].has_key?(k) && array2[i][k] == v } }

你可能会得到类似array1.each_with_index {| h,i |的东西h.delete_if {| k,v | array2 [i] .has_key?(k)&& array2 [i] [k] == v}}