J'essaie de comparer deux Ruby Hash en utilisant le code suivant:
#!/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 sortie à l'écran est le fichier complet de fichier2. Je sais pertinemment que les fichiers sont différents, mais le script ne semble pas le comprendre.
Vous pouvez comparer directement les hachages pour l'égalité:
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
Vous pouvez convertir les hachages en tableaux, puis obtenir leur différence:
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}
Simplifier davantage:
Affectation de la différence via une structure ternaire:
difference = (hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
=> [["c", 3]]
Hash[*difference.flatten]
=> {"c"=>3}
Faire tout cela en une seule opération et se débarrasser de la variable difference
:
Hash[*(
(hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
).flatten]
=> {"c"=>3}
Vous pouvez essayer la gemme hashdiff , qui permet une comparaison en profondeur des hachages et des tableaux dans le hachage.
Ce qui suit est un exemple:
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]]
Si vous voulez connaître la différence entre deux hachages, procédez comme suit:
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"}
Vous pouvez utiliser une simple intersection de tableau pour savoir ce qui diffère dans chaque hachage.
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
J'ai eu le même problème et j'ai envoyé une demande de pull à Rails
https://github.com/elfassy/Rails/commit/5f3410e04fe8c4d4745397db866c9633b80ccec6
Si vous avez besoin d'un diff sale et sale entre les hachages qui prend correctement en charge les valeurs nulles, vous pouvez utiliser quelque chose comme:
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
Si vous voulez un diff joliment formaté, vous pouvez faire ceci:
# Gemfile
gem 'awesome_print' # or gem install awesome_print
Et dans votre code:
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})
L'idée est d'utiliser des impressions impressionnantes pour formater et différer la sortie. Le diff ne sera pas exact, mais il est utile pour le débogage.
Ceci a été répondu dans " Comparant Ruby hash) ". Rails ajoute une méthode diff
) à hachage. Cela fonctionne bien.
... et maintenant sous module sous forme à appliquer à diverses classes de collection (hachage entre elles). Ce n'est pas une inspection en profondeur, mais c'est 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
qu'en est-il de convertir les deux hash to_json et de les comparer en tant que chaîne? mais en gardant à l'esprit que
require "json"
h1 = {a: 20}
h2 = {a: "20"}
h1.to_json==h1.to_json
=> true
h1.to_json==h2.to_json
=> false