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 enarray2
- Hashes parte de
array2
que no existe enarray1
- 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)
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