Как сравнить два хэша? - PullRequest
95 голосов
/ 08 февраля 2011

Я пытаюсь сравнить два хэша Ruby, используя следующий код:

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

Вывод на экран - полный файл из файла file2.Я точно знаю, что файлы разные, но сценарий, похоже, не подхватывает их.

Ответы [ 11 ]

147 голосов
/ 08 февраля 2011

Вы можете сравнить хэши напрямую на равенство:

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


Вы можете преобразовать хэши в массивы, а затем получить их разницу:

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}

Упрощение далее:

Назначение разницы через троичную структуру:

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

Выполнение всего за одну операцию и избавление от переменной difference:

  Hash[*(
  (hash3.size > hash1.size)    \
      ? hash3.to_a - hash1.to_a \
      : hash1.to_a - hash3.to_a
  ).flatten] 
=> {"c"=>3}
31 голосов
/ 22 июня 2012

Вы можете попробовать гем hashdiff , который позволяет глубоко сравнивать хэши и массивы в хэше.

Ниже приведен пример:

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]]
15 голосов
/ 08 февраля 2011

Если вы хотите получить разницу между двумя хэшами, вы можете сделать это:

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"}
10 голосов
/ 05 марта 2014

Rails устарел метод diff.

Для быстрого однострочного:

hash1.to_s == hash2.to_s
4 голосов
/ 17 ноября 2016

Вы можете использовать простое пересечение массива, чтобы вы могли знать, что отличается в каждом хэше.

    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
1 голос
/ 31 декабря 2014

... и теперь в модуле форме для применения к различным классам коллекций (среди них Hash).Это не глубокая проверка, но это просто.

# 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 голос
/ 10 октября 2014

Если вы хотите хорошо отформатированный diff, вы можете сделать это:

# Gemfile
gem 'awesome_print' # or gem install awesome_print

А в вашем коде:

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})

Идея состоит в том, чтобы использовать потрясающую печать для форматирования и вывода результатов Разница не будет точной, но она полезна для целей отладки.

1 голос
/ 04 октября 2013

Если вам нужен быстрый и грязный diff между хешами, который правильно поддерживает nil в значениях, вы можете использовать что-то вроде

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 голос
/ 08 апреля 2013

У меня была такая же проблема, и я отправил запрос на извлечение на рельсы

  • Работает с глубоко вложенным хешем
  • Работает с массивами хэшей

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

1 голос
/ 27 декабря 2011

Ответ на этот вопрос дан в " Сравнении хэшей в рубине ".Rails добавляет diff метод к хешам.Хорошо работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...