-
-
Save agius/2631752 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby | |
require 'rubygems' | |
require 'json' | |
unless ARGV.count > 1 | |
puts "Usage: json_diff.rb json_file_1.json json_file_2.json" | |
exit | |
end | |
def different?(a, b, bi_directional=true) | |
return [a.class.name, nil] if !a.nil? && b.nil? | |
return [nil, b.class.name] if !b.nil? && a.nil? | |
differences = {} | |
a.each do |k, v| | |
if !v.nil? && b[k].nil? | |
differences[k] = [v, nil] | |
next | |
elsif !b[k].nil? && v.nil? | |
differences[k] = [nil, b[k]] | |
next | |
end | |
if v.is_a?(Hash) | |
unless b[k].is_a?(Hash) | |
differences[k] = "Different types" | |
next | |
end | |
diff = different?(a[k], b[k]) | |
differences[k] = diff if !diff.nil? && diff.count > 0 | |
elsif v.is_a?(Array) | |
unless b[k].is_a?(Array) | |
differences[k] = "Different types" | |
next | |
end | |
c = 0 | |
diff = v.map do |n| | |
if n.is_a?(Hash) | |
diffs = different?(n, b[k][c]) | |
c += 1 | |
["Differences: ", diffs] unless diffs.nil? | |
else | |
c += 1 | |
[n , b[c]] unless b[c] == n | |
end | |
end.compact | |
differences[k] = diff if diff.count > 0 | |
else | |
differences[k] = [v, b[k]] unless v == b[k] | |
end | |
end | |
return differences if !differences.nil? && differences.count > 0 | |
end | |
json_a = JSON.parse(File.read(ARGV[0])) | |
json_b = JSON.parse(File.read(ARGV[1])) | |
differences = different?(json_a, json_b) | |
if ARGV[2] | |
File.open(ARGV[2], 'w'){ |f| f.write(JSON.pretty_generate(differences)) } | |
else | |
puts JSON.pretty_generate(differences) | |
end |
Using this in a script, thanks a ton, very handy.
Legit. +1
Hey agius, thanks! nice code!
I spotted a bug when comparing quite complex nested structures. Here's simplified test case to reproduce the bug behavior:
irb(main):001:0> h = {"a" => [11,22,33] }
=> {"a"=>[11, 22, 33]}
irb(main):002:0> different?( h, h) # <--- we compare h to itself
=> {"a"=>[[11, nil], [22, nil], [33, nil]]} # <--- we got not nil, which is an error
Here's fixed code:
diff = v.map do |n|
if n.is_a?(Hash)
diffs = different?(n, b[k][c])
c += 1
["Differences: ", diffs] if !diffs.nil? && diffs.count>0
else
c += 1
#[n , b[c]] unless b[c] == n # OLD BUGGY LINE (missing [k] and going 1 item ahead due to c+=1 above)
[n , b[c-1]] unless b[k][c-1] == n # NEW SHINY BUGLESS THING!-)
end
end.compact
and now let us replay the test case:
irb(main):001:0> h = {"a" => [11,22,33] }
=> {"a"=>[11, 22, 33]}
irb(main):002:0> different?( h, h)
=> nil
here you go!
thanx again..
ops! another bug: since you iterate over 1st object (of two objects to compare), your function won't notice if 2nd object have all the same elements as the 1st one PLUS some extra elements.
look:
irb(main):012:0> h1 = {"a" => [11,22,33] }
=> {"a"=>[11, 22, 33]}
irb(main):013:0> h2 = {"a" => [11,22,33,44] } # <--- h2 is same as h1 PLUS one more extra element
=> {"a"=>[11, 22, 33, 44]}
irb(main):014:0> different?( h1, h2) # <--- we iterate h1 (smaller one)
=> nil # and we spotted NO difference!
irb(main):015:0> different?( h2, h1) # only if we compare them backwards
=> {"a"=>[[44, nil]]} # then we have our diff
so temp workaround would be to wrap your f-n and wrapper will call it twice (h1,h2) and (h2,h1), but then functions results representation if diffs are present would be ugly.. It is ok for my case as I'm only interested if they're exact same or not. (I don't care about diffs:)
thanks
Hey awesome, i used this! (linked back to your gist in src)