web-dev-qa-db-de.com

Wie vergleiche ich zwei Hashes?

Ich versuche, zwei Ruby Hashes mit dem folgenden Code zu vergleichen:

#!/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

Die Ausgabe auf dem Bildschirm ist die vollständige Datei aus Datei2. Ich weiß, dass die Dateien unterschiedlich sind, aber das Skript scheint es nicht zu verstehen.

95
dennismonsewicz

Sie können Hashes direkt auf Gleichheit vergleichen:

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


Sie können die Hashes in Arrays konvertieren und deren Differenz ermitteln:

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}

Weiter vereinfachen:

Differenzzuweisung über eine ternäre Struktur:

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

Alles in einem Arbeitsgang erledigen und die Variable difference entfernen:

  Hash[*(
  (hash3.size > hash1.size)    \
      ? hash3.to_a - hash1.to_a \
      : hash1.to_a - hash3.to_a
  ).flatten] 
=> {"c"=>3}
147
the Tin Man

Sie können den Edelstein hashdiff ausprobieren, der einen umfassenden Vergleich von Hashes und Arrays im Hash ermöglicht.

Folgendes ist ein Beispiel:

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

Wenn Sie wissen möchten, was der Unterschied zwischen zwei Hashes ist, können Sie dies tun:

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

Rails ist veraltet die diff Methode.

Für einen schnellen Einzeiler:

hash1.to_s == hash2.to_s
10
Evan

Sie können eine einfache Array-Schnittmenge verwenden, um festzustellen, was sich in den einzelnen Hashes unterscheidet.

    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

Ich hatte das gleiche Problem und schickte eine Pull-Anfrage an Rails

  • Funktioniert mit tief verschachteltem Hash
  • Funktioniert mit Arrays von Hashes

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

1
montrealmike

Wenn Sie einen schnellen und fehlerhaften Unterschied zwischen Hashes benötigen, der Nullwerte korrekt unterstützt, können Sie so etwas wie verwenden

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

Wenn Sie ein gut formatiertes Diff möchten, können Sie dies tun:

# Gemfile
gem 'awesome_print' # or gem install awesome_print

Und in Ihrem 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})

Die Idee ist es, mit großartigem Druck die Ausgabe zu formatieren und zu differenzieren. Der Diff wird nicht genau sein, ist aber für Debugging-Zwecke nützlich.

1

Dies wurde mit " Vergleichen von Ruby Hashes " beantwortet. Rails fügt eine diff-Methode hinz zu Hashes. Es funktioniert gut.

1
Wolfram Arnold

... und jetzt in Modul Form, um auf eine Vielzahl von Sammlungsklassen angewendet zu werden (unter anderem Hash). Es ist keine tiefe Inspektion, aber es ist einfach.

# 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

was ist mit der Konvertierung von Hash to_json und dem Vergleich als String? aber bedenke das

require "json"
h1 = {a: 20}
h2 = {a: "20"}

h1.to_json==h1.to_json
=> true
h1.to_json==h2.to_json
=> false
0
stbnrivas