2011-02-08 24 views
83

Estoy tratando de comparar dos valores hash Rubí usando el siguiente código:¿Cómo comparo dos hashes?

#!/usr/bin/env ruby 

require "yaml" 
require "active_support" 

file1 = YAML::load(File.open('./en_20110207.yml')) 
file2 = YAML::load(File.open('./locales/en.yml')) 

arr = [] 

file1.select { |k,v| 
    file2.select { |k2, v2| 
    arr << "#{v2}" if "#{v}" != "#{v2}" 
    } 
} 

puts arr 

La salida a la pantalla es el archivo completo del archivo 2. Sé a ciencia cierta que los archivos son diferentes, pero el guión parece no entenderlo.

+0

posible duplicado de [Al comparar los hashes de rubí] (http://stackoverflow.com/questions/1766741/comparing-ruby-hashes) –

Respuesta

123

Puede comparar los valores hash directamente por la igualdad:

hash1 = {'a' => 1, 'b' => 2} 
hash2 = {'a' => 1, 'b' => 2} 
hash3 = {'a' => 1, 'b' => 2, 'c' => 3} 

hash1 == hash2 # => true 
hash1 == hash3 # => false 

hash1.to_a == hash2.to_a # => true 
hash1.to_a == hash3.to_a # => false 


Puede convertir los valores hash de matrices, a continuación, obtener su diferencia:

hash3.to_a - hash1.to_a # => [["c", 3]] 

if (hash3.size > hash1.size) 
    difference = hash3.to_a - hash1.to_a 
else 
    difference = hash1.to_a - hash3.to_a 
end 
Hash[*difference.flatten] # => {"c"=>3} 

simplificando aún más:

Asignación de diferencia a través de una estructura ternaria:

difference = (hash3.size > hash1.size) \ 
       ? hash3.to_a - hash1.to_a \ 
       : hash1.to_a - hash3.to_a 
=> [["c", 3]] 
    Hash[*difference.flatten] 
=> {"c"=>3} 

hacerlo todo en una sola operación y deshacerse de la difference variables:

Hash[*(
    (hash3.size > hash1.size) \ 
     ? hash3.to_a - hash1.to_a \ 
     : hash1.to_a - hash3.to_a 
).flatten] 
=> {"c"=>3} 
+2

¿Hay alguna forma de obtener las diferencias entre los dos? – dennismonsewicz

+0

@dennismonsewicz Ver la edición. –

+0

- ¡Muchas gracias por la ayuda! – dennismonsewicz

-3

¿Qué hay de otra, el enfoque más simple:

require 'fileutils' 
FileUtils.cmp(file1, file2) 
+1

Eso sólo es significativo si necesita los hashes ser idéntico en el disco. Dos archivos que son diferentes en el disco porque los elementos hash están en diferentes órdenes, aún pueden contener los mismos elementos, y serán iguales en lo que respecta a Ruby una vez que se carguen. –

11

Si quiere saber cuál es la diferencia entre dos valores hash, puede hacer esto:

h1 = {:a => 20, :b => 10, :c => 44} 
h2 = {:a => 2, :b => 10, :c => "44"} 
result = {} 
h1.each {|k, v| result[k] = h2[k] if h2[k] != v } 
p result #=> {:a => 2, :c => "44"} 
1

Esto fue respondido en "Comparing ruby hashes". Rails agrega un diff method a hashes. Funciona bien.

+3

[Método Diff] (http://apidock.com/rails/Hash/diff) está en desuso a partir de las versiones de Rails posteriores a v4.0.2. –

27

Puede probar la gema hashdiff, que permite una comparación profunda de hashes y matrices en el hash.

El siguiente es un ejemplo:

a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}} 
b = {a:{y:3}, b:{y:3, z:30}} 

diff = HashDiff.diff(a, b) 
diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]] 
+2

Tuve algunos hashes bastante profundos que causan fallas de prueba. Al reemplazar el 'got_hash.should eql expected_hash' con' HashDiff.diff (got_hash, expected_hash) .should eql [] 'Ahora obtengo una salida que muestra exactamente lo que necesito. ¡Perfecto! – dukedave

+0

Guau, HashDiff es increíble. Hizo un trabajo rápido de tratar de ver qué ha cambiado en una gran matriz JSON anidada. ¡Gracias! –

+0

¡Tu joya es increíble! Muy útil cuando se escriben especificaciones que involucran manipulaciones JSON. Gracias. – Alain

1

Si necesita un diff rápida y sucia entre los hashes que soporta correctamente nula en valores que se pueden usar algo como

def diff(one, other) 
    (one.keys + other.keys).uniq.inject({}) do |memo, key| 
    unless one.key?(key) && other.key?(key) && one[key] == other[key] 
     memo[key] = [one.key?(key) ? one[key] : :_no_key, other.key?(key) ? other[key] : :_no_key] 
    end 
    memo 
    end 
end 
6

Carriles es deprecating el método diff.

Para una rápida de una sola línea:

hash1.to_s == hash2.to_s 
+0

Siempre me olvido de esto. Hay muchas verificaciones de igualdad que son fáciles de usar con 'to_s'. –

+5

Fallará cuando hashes iguales tengan claves en un orden diferente: '{a: 1, b: 2} == {b: 2, a: 1}' => verdadero, '{a: 1, b: 2} .to_s == {b: 2, a: 1} .to_s' => falso – aidan

1

Si desea un diff con un formato agradable, usted puede hacer esto:

# Gemfile 
gem 'awesome_print' # or gem install awesome_print 

Y en el código:

require 'ap' 

def my_diff(a, b) 
    as = a.ai(plain: true).split("\n").map(&:strip) 
    bs = b.ai(plain: true).split("\n").map(&:strip) 
    ((as - bs) + (bs - as)).join("\n") 
end 

puts my_diff({foo: :bar, nested: {val1: 1, val2: 2}, end: :v}, 
      {foo: :bar, n2: {nested: {val1: 1, val2: 3}}, end: :v}) 

La idea es utilizar una impresionante impresión para formatear y diferir la salida. La diferencia no será exacta, pero es útil para fines de depuración.

0

... y ahora en module formulario que se aplicará a una variedad de clases de colección (hash entre ellas). No es una inspección profunda, pero es simple.

# Enable "diffing" and two-way transformations between collection objects 
module Diffable 
    # Calculates the changes required to transform self to the given collection. 
    # @param b [Enumerable] The other collection object 
    # @return [Array] The Diff: A two-element change set representing items to exclude and items to include 
    def diff(b) 
    a, b = to_a, b.to_a 
    [a - b, b - a] 
    end 

    # Consume return value of Diffable#diff to produce a collection equal to the one used to produce the given diff. 
    # @param to_drop [Enumerable] items to exclude from the target collection 
    # @param to_add [Enumerable] items to include in the target collection 
    # @return [Array] New transformed collection equal to the one used to create the given change set 
    def apply_diff(to_drop, to_add) 
    to_a - to_drop + to_add 
    end 
end 

if __FILE__ == $0 
    # Demo: Hashes with overlapping keys and somewhat random values. 
    Hash.send :include, Diffable 
    rng = Random.new 
    a = (:a..:q).to_a.reduce(Hash[]){|h,k| h.merge! Hash[k, rng.rand(2)] } 
    b = (:i..:z).to_a.reduce(Hash[]){|h,k| h.merge! Hash[k, rng.rand(2)] } 
    raise unless a == Hash[ b.apply_diff(*b.diff(a)) ] # change b to a 
    raise unless b == Hash[ a.apply_diff(*a.diff(b)) ] # change a to b 
    raise unless a == Hash[ a.apply_diff(*a.diff(a)) ] # change a to a 
    raise unless b == Hash[ b.apply_diff(*b.diff(b)) ] # change b to b 
end 
3

Puede utilizar una intersección de matriz simple, de esta manera usted puede saber qué difiere en cada hash.

hash1 = { a: 1 , b: 2 } 
    hash2 = { a: 2 , b: 2 } 

    overlapping_elements = hash1.to_a & hash2.to_a 

    exclusive_elements_from_hash1 = hash1.to_a - overlapping_elements 
    exclusive_elements_from_hash2 = hash2.to_a - overlapping_elements 
Cuestiones relacionadas