web-dev-qa-db-fra.com

Comment comparer deux hachages?

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.

95
dennismonsewicz

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}
147
the Tin Man

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]]
31
liu fengyun

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"}
15
Guilherme Bernal

Rails est la méthode dépréciée la diff .

Pour un one-liner rapide:

hash1.to_s == hash2.to_s
10
Evan

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
4
ErvalhouS

J'ai eu le même problème et j'ai envoyé une demande de pull à Rails

  • Fonctionne avec un hachage profondément imbriqué
  • Fonctionne avec des tableaux de hachages

https://github.com/elfassy/Rails/commit/5f3410e04fe8c4d4745397db866c9633b80ccec6

1
montrealmike

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
1
dolzenko

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.

1
Benjamin Crouzier

Ceci a été répondu dans " Comparant Ruby hash) ". Rails ajoute une méthode diff) à hachage. Cela fonctionne bien.

1
Wolfram Arnold

... 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
1
Iron Savior

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
0
stbnrivas