Как я понимаю, вам даны два массива хэшей и набор ключей. Вы хотите отклонить все элементы (хэши) первого массива, значения которого соответствуют значениям любого элемента (хэша) второго массива для всех указанных ключей. Вы можете сделать это следующим образом.
код
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
Линия:
(keys-h.keys).empty? && set2.include?(h.values_at(*keys))
может быть упрощено до:
set2.include?(h.values_at(*keys))
, если ни одно из значений ключей в элементах (хешей) из array1
: nil
. Я создал набор (а не массив) от array2
, чтобы ускорить поиск h.values_at(*keys)
в этой строке.
Пример
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}]
Объяснение
Сначала нужно создать 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 первый элемент 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
так вычислить:
s << h.values_at(*keys)
#=> s << {:a=>"1", :b=>"2", :c=>"10"}.values_at(*[:a,:b] }
#=> s << ["1","2"] => #<Set: {["1", "2"]}>
Pass второй (последний) элемент e0
к блоку:
h,s = e0.next
#=> [{:a=>"3", :b=>"5", :c=>"6"}, #<Set: {["1", "2"]}>]
(keys-h.keys).empty?
#=> true
так вычислить:
s << h.values_at(*keys)
#=> #<Set: {["1", "2"], ["3", "5"]}>
set2
#=> #<Set: {["1", "2"], ["3", "5"]}>
Отклонить элементы array1
Теперь мы перебираем array1
, отвергая элементы, для которых блок принимает значение true
.
e1 = array1.reject
#=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"3"},
# {:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}]:reject>
Первый элемент 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
поэтому первый элемент 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
так что второй элемент e1
не отклоняется. И наконец:
h = e1.next
#=> {:a=>1, :c=>4}
a = (keys-h.keys).empty?
#=> ([:a,:c]-[:a,:b]).empty? => [:c].empty? => false
так возвращает истину (то есть последний элемент e1
не отвергается), так как нет необходимости вычислять:
b = set2.include?(h.values_at(*keys))
Ваш вопрос остается неясным. Как вы собираетесь получить этот результат? Что с этим связаны ключи? – engineersmnky
Вы можете освободить трюм, если вы его уточните. Если первый абзац моего ответа является точным, не стесняйтесь его использовать, изменить или * verbayim *. Кроме того, я предлагаю вам объяснить, почему в вашем примере 'array1.last' хранился, но' array1.first' не был. –