rails example create array ruby set data-comparison

example - Ruby-Show Deltas Between 2 array of hashes basado en un subconjunto de claves hash



ruby hash (3)

Esto no es muy bonito, pero funciona. Crea una tercera matriz que contiene todos los valores únicos tanto en la array1 como en la array2 y recorre todo eso.

Entonces, ¿desde include? no permite un matcher personalizado, podemos simularlo usando detect y buscando un elemento en el array que tenga la coincidencia de sub-hash personalizada. Vamos a envolverlo en un método personalizado para que podamos llamarlo pasar en array1 o array2 lugar de escribirlo dos veces.

Finalmente, recorremos nuestro array3 y determinamos si el item proviene de array1 , array2 o ambos y lo agregamos a la matriz de salida correspondiente.

array1 = [{''id'' => ''1'', ''ref'' => ''1001'', ''name'' => ''CA'', ''extra'' => ''Not Sorted On 5''}, {''id'' => ''2'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 9''}, {''id'' => ''7'', ''ref'' => ''1007'', ''name'' => ''OR'', ''extra'' => ''Not Sorted On 11''}] array2 = [{''id'' => ''1'', ''ref'' => ''1001'', ''name'' => ''CA'', ''extra'' => ''Not Sorted On 5''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 9''}, {''id'' => ''8'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''5'', ''ref'' => ''1005'', ''name'' => ''MT'', ''extra'' => ''Not Sorted On 10''}, {''id'' => ''12'', ''ref'' => ''1012'', ''name'' => ''TX'', ''extra'' => ''Not Sorted On 85''}] # combine the arrays into 1 array that contains items in both array1 and array2 to loop through array3 = (array1 + array2).uniq { |item| { ''id'' => item[''id''], ''ref'' => item[''ref''], ''name'' => item[''name''] } } # Array#include? doesn''t allow a custom matcher, so we can fake it by using Array#detect def is_included_in(array, object) object_identifier = { ''id'' => object[''id''], ''ref'' => object[''ref''], ''name'' => object[''name''] } array.detect do |item| { ''id'' => item[''id''], ''ref'' => item[''ref''], ''name'' => item[''name''] } == object_identifier end end # output array initializing array1_only = [] array2_only = [] array1_and_array2 = [] # loop through all items in both array1 and array2 and check if it was in array1 or array2 # if it was in both, add to array1_and_array2, otherwise, add it to the output array that # corresponds to the input array array3.each do |item| in_array1 = is_included_in(array1, item) in_array2 = is_included_in(array2, item) if in_array1 && in_array2 array1_and_array2.push item elsif in_array1 array1_only.push item else array2_only.push item end end puts array1_only.inspect # => [{"id"=>"2", "ref"=>"1002", "name"=>"NY", "extra"=>"Not Sorted On 7"}, {"id"=>"7", "ref"=>"1007", "name"=>"OR", "extra"=>"Not Sorted On 11"}] puts array2_only.inspect # => [{"id"=>"8", "ref"=>"1002", "name"=>"NY", "extra"=>"Not Sorted On 7"}, {"id"=>"5", "ref"=>"1005", "name"=>"MT", "extra"=>"Not Sorted On 10"}, {"id"=>"12", "ref"=>"1012", "name"=>"TX", "extra"=>"Not Sorted On 85"}] puts array1_and_array2.inspect # => [{"id"=>"1", "ref"=>"1001", "name"=>"CA", "extra"=>"Not Sorted On 5"}, {"id"=>"3", "ref"=>"1003", "name"=>"WA", "extra"=>"Not Sorted On 9"}]

Estoy intentando comparar dos matrices de hashes con una estructura hash muy similar (claves idénticas y siempre presentes) y devolver los deltas entre los dos, específicamente, me gustaría capturar lo siguiente:

  • Hashes parte de array1 que no existe en array2
  • Hashes parte de array2 que no existe en array1
  • Hashes que aparecen en ambos conjuntos de datos

Esto típicamente se puede lograr simplemente haciendo lo siguiente:

deltas_old_new = (array1-array2) deltas_new_old = (array2-array1)

El problema para mí (¡que se ha convertido en una lucha de 2-3 horas!) Es que necesito identificar los deltas basados ​​en los valores de 3 claves dentro del hash (''id'', ''ref'', ''name'') - los valores de estas 3 claves son efectivamente lo que hace una entrada única en mis datos, pero debo retener los otros pares clave / valor del hash (por ejemplo, ''extra'' y muchos otros pares clave / valor no mostrados para abreviar).

Ejemplo de datos:

array1 = [{''id'' => ''1'', ''ref'' => ''1001'', ''name'' => ''CA'', ''extra'' => ''Not Sorted On 5''}, {''id'' => ''2'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 9''}, {''id'' => ''7'', ''ref'' => ''1007'', ''name'' => ''OR'', ''extra'' => ''Not Sorted On 11''}] array2 = [{''id'' => ''1'', ''ref'' => ''1001'', ''name'' => ''CA'', ''extra'' => ''Not Sorted On 5''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 9''}, {''id'' => ''8'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''5'', ''ref'' => ''1005'', ''name'' => ''MT'', ''extra'' => ''Not Sorted On 10''}, {''id'' => ''12'', ''ref'' => ''1012'', ''name'' => ''TX'', ''extra'' => ''Not Sorted On 85''}]

Resultado esperado (3 matriz separada de hashes):

Objeto que contiene datos en array1 pero no en array2 -

[{''id'' => ''2'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''7'', ''ref'' => ''1007'', ''name'' => ''OR'', ''extra'' => ''Not Sorted On 11''}]

Objeto que contiene datos en array2 pero no en array1 -

[{''id'' => ''8'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''5'', ''ref'' => ''1005'', ''name'' => ''MT'', ''extra'' => ''Not Sorted On 10''}, {''id'' => ''12'', ''ref'' => ''1012'', ''name'' => ''TX'', ''extra'' => ''Not Sorted On 85''}]

Objeto que contiene datos en BOTH array1 y array2 -

[{''id'' => ''1'', ''ref'' => ''1001'', ''name'' => ''CA'', ''extra'' => ''Not Sorted On 5''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 9''}]

Intenté numerosos intentos para comparar la iteración en las matrices y el uso de Hash#keep_if base a las 3 claves, así como la combinación de ambos conjuntos de datos en una única matriz y luego intentar Hash#keep_if de la matriz 1 pero array1 llegando con las manos vacías . ¡Gracias de antemano por su tiempo y asistencia!


Para este tipo de problema, generalmente es más fácil trabajar con índices.

Código

def keepers(array1, array2, keys) a1 = make_hash(array1, keys) a2 = make_hash(array2, keys) common_keys_of_a1_and_a2 = a1.keys & a2.keys [keeper_idx(array1, a1, common_keys_of_a1_and_a2), keeper_idx(array2, a2, common_keys_of_a1_and_a2)] end def make_hash(arr, keys) arr.each_with_index.with_object({}) do |(g,i),h| (h[g.values_at(*keys)] ||= []) << i end end def keeper_idx(arr, a, common_keys_of_a1_and_a2) arr.size.times.to_a - a.values_at(*common_keys_of_a1_and_a2).flatten end

Ejemplo

array1 = [{''id'' => ''1'', ''ref'' => ''1001'', ''name'' => ''CA'', ''extra'' => ''Not Sorted On 5''}, {''id'' => ''2'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 9''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 8''}, {''id'' => ''7'', ''ref'' => ''1007'', ''name'' => ''OR'', ''extra'' => ''Not Sorted On 11''}] array2 = [{''id'' => ''1'', ''ref'' => ''1001'', ''name'' => ''CA'', ''extra'' => ''Not Sorted On 5''}, {''id'' => ''3'', ''ref'' => ''1003'', ''name'' => ''WA'', ''extra'' => ''Not Sorted On 9''}, {''id'' => ''8'', ''ref'' => ''1002'', ''name'' => ''NY'', ''extra'' => ''Not Sorted On 7''}, {''id'' => ''5'', ''ref'' => ''1005'', ''name'' => ''MT'', ''extra'' => ''Not Sorted On 10''}, {''id'' => ''5'', ''ref'' => ''1005'', ''name'' => ''MT'', ''extra'' => ''Not Sorted On 12''}, {''id'' => ''12'', ''ref'' => ''1012'', ''name'' => ''TX'', ''extra'' => ''Not Sorted On 85''}]

Tenga en cuenta que las dos matrices son ligeramente diferentes de las que figuran en la pregunta. La pregunta no especificaba si cada matriz podía contener dos hashes; los mismos valores para las claves especificadas. Por lo tanto, agregué un hash a cada matriz para mostrar que se ha tratado ese caso.

keys = [''id'', ''ref'', ''name''] idx1, idx2 = keepers(array1, array2, keys) #=> [[1, 4], [2, 3, 4, 5]]

idx1 ( idx2 ) son los índices de los elementos de array1 ( array2 ) que quedan después de eliminar las coincidencias. ( array1 y array2 no se modifican, sin embargo).

Se deduce que las dos matrices se asignan a

array1.values_at(*idx1) #=> [{"id"=> "2", "ref"=>"1002", "name"=>"NY", "extra"=>"Not Sorted On 7"}, # {"id"=> "7", "ref"=>"1007", "name"=>"OR", "extra"=>"Not Sorted On 11"}]

y

array2.values_at(*idx2) #=> [{"id"=> "8", "ref"=>"1002", "name"=>"NY", "extra"=>"Not Sorted On 7"}, # {"id"=> "5", "ref"=>"1005", "name"=>"MT", "extra"=>"Not Sorted On 10"}, # {"id"=> "5", "ref"=>"1005", "name"=>"MT", "extra"=>"Not Sorted On 12"}, # {"id"=>"12", "ref"=>"1012", "name"=>"TX", "extra"=>"Not Sorted On 85"}]

Los índices de los hashes que se eliminan se dan de la siguiente manera.

array1.size.times.to_a - idx1 #=> [0, 2, 3] array2.size.times.to_a - idx2 #[0, 1]

Explicación

Los pasos son los siguientes.

a1 = make_hash(array1, keys) #=> {["1", "1001", "CA"]=>[0], ["2", "1002", "NY"]=>[1], # ["3", "1003", "WA"]=>[2, 3], ["7", "1007", "OR"]=>[4]} a2 = make_hash(array2, keys) #=> {["1", "1001", "CA"]=>[0], ["3", "1003", "WA"]=>[1], # ["8", "1002", "NY"]=>[2], ["5", "1005", "MT"]=>[3, 4], # ["12", "1012", "TX"]=>[5]} common_keys_of_a1_and_a2 = a1.keys & a2.keys #=> [["1", "1001", "CA"], ["3", "1003", "WA"]] keeper_idx(array1, a1, common_keys_of_a1_and_a2) #=> [1, 4] (for array1) keeper_idx(array2, a2, common_keys_of_a1_and_a2) #=> [2, 3, 4, 5]· (for array2)


Ver Array#- y Array#&

array1 - array2 #data in array1 but not in array2 array2 - array1 #data in array2 but not in array1 array1 & array2 #data in both array1 and array2

Como ha etiquetado este conjunto de preguntas, puede usar conjuntos de manera similar:

require ''set'' set1 = array1.to_set set2 = array2.to_set set1 - set2 set2 - set1 set1 & set2