I am trying to compare two Ruby Hashes using the following code:
我正在尝试使用以下代码来比较两个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
The output to the screen is the full file from file2. I know for a fact that the files are different, but the script doesn't seem to pick it up.
屏幕的输出是来自file2的完整文件。我知道这些文件是不同的,但是脚本似乎没有找到它。
11 个解决方案
#1
132
You can compare hashes directly for equality:
您可以直接将散列与等式进行比较:
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
You can convert the hashes to arrays, then get their difference:
可以将散列转换为数组,然后得到它们的差值:
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}
Simplifying further:
进一步简化:
Assigning difference via a ternary structure:
通过三元结构分配差异:
difference = (hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
=> [["c", 3]]
Hash[*difference.flatten]
=> {"c"=>3}
Doing it all in one operation and getting rid of the difference
variable:
在一次操作中完成所有操作,去掉差分变量:
Hash[*(
(hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
).flatten]
=> {"c"=>3}
#2
28
You can try the hashdiff gem, which allows deep comparison of hashes and arrays in the hash.
您可以尝试hashdiff gem,它允许对散列中的散列和数组进行深入的比较。
The following is an example:
下面是一个例子:
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]]
#3
13
If you want to get what is the difference between two hashes, you can do this:
如果你想知道两个散列之间的区别,你可以这样做:
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"}
#4
8
Rails is deprecating the diff
method.
Rails不赞成diff方法。
For a quick one-liner:
快速一行程序:
hash1.to_s == hash2.to_s
#5
3
You could use a simple array intersection, this way you can know what differs in each 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
#6
1
This was answered in "Comparing ruby hashes". Rails adds a diff
method to hashes. It works well.
这在“比较ruby散列”中得到了回答。Rails向散列添加了一个diff方法。它的工作原理。
#7
1
I had the same problem and sent a pull request to rails
我遇到了同样的问题,并向rails发送了一个拉请求
- Works with deeply nested hash
- 使用深度嵌套散列
- Works with arrays of hashes
- 处理哈希数组
https://github.com/elfassy/rails/commit/5f3410e04fe8c4d4745397db866c9633b80ccec6
https://github.com/elfassy/rails/commit/5f3410e04fe8c4d4745397db866c9633b80ccec6
#8
1
If you need a quick and dirty diff between hashes which correctly supports nil in values you can use something like
如果您需要在正确支持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
#9
1
If you want a nicely formatted diff, you can do this:
如果您想要一个格式良好的diff,您可以这样做:
# Gemfile
gem 'awesome_print' # or gem install awesome_print
And in your 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})
The idea is to use awesome print to format, and diff the output. The diff won't be exact, but it is useful for debugging purposes.
我们的想法是使用很棒的打印来格式化,并减少输出。这个差异不会很精确,但是对于调试很有用。
#10
0
... and now in module form to be applied to a variety of collection classes (Hash among them). It's not a deep inspection, but it's 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
#11
-3
How about another, simpler approach:
另一种更简单的方法呢?
require 'fileutils'
FileUtils.cmp(file1, file2)
#1
132
You can compare hashes directly for equality:
您可以直接将散列与等式进行比较:
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
You can convert the hashes to arrays, then get their difference:
可以将散列转换为数组,然后得到它们的差值:
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}
Simplifying further:
进一步简化:
Assigning difference via a ternary structure:
通过三元结构分配差异:
difference = (hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
=> [["c", 3]]
Hash[*difference.flatten]
=> {"c"=>3}
Doing it all in one operation and getting rid of the difference
variable:
在一次操作中完成所有操作,去掉差分变量:
Hash[*(
(hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
).flatten]
=> {"c"=>3}
#2
28
You can try the hashdiff gem, which allows deep comparison of hashes and arrays in the hash.
您可以尝试hashdiff gem,它允许对散列中的散列和数组进行深入的比较。
The following is an example:
下面是一个例子:
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]]
#3
13
If you want to get what is the difference between two hashes, you can do this:
如果你想知道两个散列之间的区别,你可以这样做:
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"}
#4
8
Rails is deprecating the diff
method.
Rails不赞成diff方法。
For a quick one-liner:
快速一行程序:
hash1.to_s == hash2.to_s
#5
3
You could use a simple array intersection, this way you can know what differs in each 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
#6
1
This was answered in "Comparing ruby hashes". Rails adds a diff
method to hashes. It works well.
这在“比较ruby散列”中得到了回答。Rails向散列添加了一个diff方法。它的工作原理。
#7
1
I had the same problem and sent a pull request to rails
我遇到了同样的问题,并向rails发送了一个拉请求
- Works with deeply nested hash
- 使用深度嵌套散列
- Works with arrays of hashes
- 处理哈希数组
https://github.com/elfassy/rails/commit/5f3410e04fe8c4d4745397db866c9633b80ccec6
https://github.com/elfassy/rails/commit/5f3410e04fe8c4d4745397db866c9633b80ccec6
#8
1
If you need a quick and dirty diff between hashes which correctly supports nil in values you can use something like
如果您需要在正确支持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
#9
1
If you want a nicely formatted diff, you can do this:
如果您想要一个格式良好的diff,您可以这样做:
# Gemfile
gem 'awesome_print' # or gem install awesome_print
And in your 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})
The idea is to use awesome print to format, and diff the output. The diff won't be exact, but it is useful for debugging purposes.
我们的想法是使用很棒的打印来格式化,并减少输出。这个差异不会很精确,但是对于调试很有用。
#10
0
... and now in module form to be applied to a variety of collection classes (Hash among them). It's not a deep inspection, but it's 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
#11
-3
How about another, simpler approach:
另一种更简单的方法呢?
require 'fileutils'
FileUtils.cmp(file1, file2)