2015-06-01 2 views
1

у меня есть два массива хэш:Объединение двух массивов хэш на основе сравнения нескольких ключей

a1 = [{ ID: 12496, name: "Robert", email: "[email protected]" }, ...] 
a2 = [{ ID: 12496, name: "Robert", ORDER_NO: 5511426 }, ...] 

Я хотел бы найти хеши в a2 которых ID и name полей соответствуют ID и name поля запись в a1 (без ухода за email или любых других предметов, которые попадают в a2), а затем объединить значение ORDER_NO в хэш на a1. то есть в конечном итоге с:

[{ ID: 12496, name: "Robert", email: "[email protected]", ORDER_NO: 5511426 } ...] 

Кроме того, я хочу, чтобы игнорировать элементы, присутствующие в а2, но не в a1.

Я делаю следующее:

a1.each do |a1_hash| 
    matching_hash = a2.find { |a2_hash| data_matches?(a1_hash, a2_hash) } if a2.present? 
    a1_hash["ORDER_NO"] = a2_hash["ORDER_NO"] if matching_hash.present? 
    a2.delete(a2_hash) 
end 

, но есть более быстрый способ?

+0

Что произойдет, если 'a2' содержит записи, отсутствующие в' a1'? В настоящее время они будут отсутствовать в результате, но это может быть неуместно, если обе хеши гарантированно содержат точно все одинаковые комбинации id/name. –

+0

@PatrickOscity Я хотел бы игнорировать записи, не присутствующие в a1. – etdev

ответ

2

Это можно сделать довольно чисто, используя несколько встроенных методов Ruby.

a1 = [{ ID: 12496, name: "Robert", email: "[email protected]" }, 
     { ID: 12497, name: "Lola", email: "[email protected]" }, 
     { ID: 12498, name: "Hank", email: "[email protected]" }] 

a2 = [{ ID: 12497, name: "Lola", ORDER_NO: 5511427 }, 
     { ID: 12496, name: "Robert", ORDER_NO: 5511426 }] 

index = a2.group_by{|entry| [entry[:ID], entry[:name]] } 
a1.map{|entry| (index[[entry[:ID], entry[:name]]] || []).reduce(entry, :merge) } 

Результат:

[{:ID=>12496, :name=>"Robert", :email=>"[email protected]", :ORDER_NO=>5511426}, 
{:ID=>12497, :name=>"Lola", :email=>"[email protected]", :ORDER_NO=>5511427}, 
{:ID=>12498, :name=>"Hank", :email=>"[email protected]"}] 

Разбивка:

Во-первых, мы используем group_by построить таблицу записей в а2, которые потенциально могут быть объединены в записи в a1.Мы индексировать эту таблицу на идентификатор и имя ключей, так как те факторы, мы используем, чтобы определить, какие записи матча:

index = a2.group_by{|entry| [entry[:ID], entry[:name]] } 

Это дает результат:

{[12497, "Lola"]=>[{:ID=>12497, :name=>"Lola", :ORDER_NO=>5511427}], 
[12496, "Robert"]=>[{:ID=>12496, :name=>"Robert", :ORDER_NO=>5511426}]} 

Далее мы map каждый запись в a1 к своей новой форме, с порядковыми номерами в индексе объединена:

a1.map{|entry| 
    # ... 
} 

чтобы получить значение мы отображающие каждую запись в, мы начнем с getti нг массив, содержащий все значения в a2, которые пригодны для слияния с этой записью из a1:

(index[[entry[:ID], entry[:name]]] || []) 

Это будет возвращать что-то вроде [{:ID=>12497, :name=>"Lola", :ORDER_NO=>5511427}] для Лолы и пустой массив для Хэнк, который не имеет соответствующей записи в a2 ,

Затем, начиная со входа от a1, мы reduce все записи из индекса к одной хэш с помощью merge (например reduce(entry, :merge)), что приводит к записи, как {:ID=>12496, :name=>"Robert", :email=>"[email protected]", :ORDER_NO=>5511426}.

Все это может показаться немного сложным, если вы не знакомы с методами в основной библиотеке Ruby. Но как только вы понимаете простые функциональные программные понятия, такие как map и reduce, действительно не так сложно придумать простые и мощные решения, подобные этому.

0

Вы можете сделать это быстрее, поместив вещи в хэш по запрошенным атрибутам, чтобы слить их до слияния, а затем получить значения (конечно, есть предположения re. Уникальность значений на входе).

x1 = a1.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m} 
x2 = a2.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m} 
x1.merge(x2.select{|k,v| x1.key?(k)}){|k,o,n| o.merge(n)}.values 

Запуск с примера данных:

a1 = [{ ID: 12496, name: "Robert", email: "[email protected]" }] 
=> [{:ID=>12496, :name=>"Robert", :email=>"[email protected]"}] 

a2 = [{ ID: 12496, name: "Robert", ORDER_NO: 5511426 }] 
=> [{:ID=>12496, :name=>"Robert", :ORDER_NO=>5511426}] 

x1 = a1.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m} 
=> {{:ID=>12496, :name=>"Robert"}=>{:ID=>12496, :name=>"Robert", :email=>"[email protected]"}} 

x2 = a2.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m} 
=> {{:ID=>12496, :name=>"Robert"}=>{:ID=>12496, :name=>"Robert", :ORDER_NO=>5511426}} 

x1.merge(x2.select{|k,v| x1.key?(k)}){|k,o,n| o.merge(n)}.values 
=> [{:ID=>12496, :name=>"Robert", :email=>"[email protected]", :ORDER_NO=>5511426}] 
+0

Это работает с данными в моем ответе. –

1

Пусть:

a1 = [{ ID: 12496, name: "Robert", email: "[email protected]" }, 
     { ID: 12497, name: "Lola", email: "[email protected]" }, 
     { ID: 12498, name: "Hank", email: "[email protected]" }] 

a2 = [{ ID: 12497, name: "Lola", ORDER_NO: 5511427 }, 
     { ID: 12496, name: "Robert", ORDER_NO: 5511426 }] 

Я предлагаю вам первый построить хэш:

h2 = a2.each_with_object({}) { |g,h| h[[g[:ID], g[:name]]]=g[:ORDER_NO] } 
    #=> { [12497, "Lola"]=>5511427, [12496, "Robert"]=>5511426 } 

затем просто шаг через элементы от a1, добавив пары ключ-значение в случае необходимости:

a1.each do |g| 
    k = [g[:ID],g[:name]] 
    g[:ORDER_NO] = h2[k] if h2.key?(k) 
end 
a1 
    #=> [{ID: 12496, name: "Robert", email: "[email protected]", ORDER_NO: 5511426}, 
    # {ID: 12497, name: "Lola", email: "[email protected]", ORDER_NO: 5511427}, 
    # {ID: 12498, name: "Hank", email: "[email protected]"}] 

Я предположил:

  • нет двух элементов (хэши) в a1 имеют те же значения, как для ID и :name;
  • нет двух элементов (хешей) в a2 имеют одинаковые значения как для ID, так и для :name; и
  • a1 должно быть сведено к минимуму.
+0

Вы правы. Я удалил его. Спасибо, что указали это. – Bala

+0

@CarySwoveland Rly хороший образец, но у вас небольшая ошибка в коде. После последнего «каждого» метода вы должны напечатать «a1» не «a» :) +1 –

+0

Спасибо, @ Лукас. Я сделал исправление. –

Смежные вопросы