diff --git a/README.md b/README.md index 78b933f..3b35e9f 100644 --- a/README.md +++ b/README.md @@ -119,22 +119,25 @@ The `:strict` option, which defaults to `true`, specifies whether numeric types #### `:ignore_keys` -The `:ignore_keys` option allows you to specify one or more keys to ignore, which defaults to `[]` (none). Ignored keys are ignored at all levels. For example: +The `:ignore_keys` option allows you to specify one or more keys to ignore, which defaults to `[]` (none). Ignored keys are ignored at all levels in both hashes. For example: ```ruby -a = { a: 1, b: { d: 2, a: 3 }, c: 4 } -b = { a: 2, b: { d: 2, a: 7 }, c: 5 } -diff = Hashdiff.diff(a, b, ignore_keys: :a) -diff.should == [['~', 'c', 4, 5]] +a = { a: 4, g: 0, b: { a: 5, c: 6, e: 1 } } +b = { b: { a: 7, c: 3, f: 1 }, d: 8 } +diff = Hashdiff.diff(a, b, ignore_keys: %i[a f]) +diff.should == [['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'd', 8]] ``` If you wish instead to ignore keys at a particlar level you should -use a [custom comparison method](https://github.com/liufengyun/hashdiff#specifying-a-custom-comparison-method) instead. For example: +use a [custom comparison method](https://github.com/liufengyun/hashdiff#specifying-a-custom-comparison-method) instead. For example to diff only at the 2nd level of both hashes: ```ruby -a = { a: 1, b: { d: 2, a: 3 }, c: 4 } -b = { a: 2, b: { d: 2, a: 7 }, c: 5 } -diff = Hashdiff.diff(a, b) { |path, _e, _a| true if path == 'b.a' } # note '.' is the default delimiter -diff.should == [['~', 'a', 1, 2], ['~', 'c', 4, 5]] +a = { a: 4, g: 0, b: { a: 5, c: 6, e: 1 } } +b = { b: { a: 7, c: 3, f: 1 }, d: 8 } +diff = Hashdiff.diff(a, b) do |path, _e, _a| + arr = path.split('.') + true if %w[a f].include?(arr.last) && arr.size == 2 # note '.' is the default delimiter +end +diff.should == [['-', 'a', 4], ['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'd', 8]] ``` #### `:indifferent` diff --git a/changelog.md b/changelog.md index a6ed8d7..95e507b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,7 @@ # Change Log +* Fix bug in ignore_keys option #88 + ## v1.1.0 2023-12-14 * Add ignore_keys option (#86 @Matzfan) diff --git a/lib/hashdiff/compare_hashes.rb b/lib/hashdiff/compare_hashes.rb index 54e7207..14b43a2 100644 --- a/lib/hashdiff/compare_hashes.rb +++ b/lib/hashdiff/compare_hashes.rb @@ -26,7 +26,11 @@ def call(obj1, obj2, opts = {}) result = [] - opts[:ignore_keys].each { |k| common_keys.delete k } + opts[:ignore_keys].each do |k| + added_keys.delete k + common_keys.delete k + deleted_keys.delete k + end # add deleted properties deleted_keys.each do |k| diff --git a/lib/hashdiff/diff.rb b/lib/hashdiff/diff.rb index 6073c9c..bf77b4d 100644 --- a/lib/hashdiff/diff.rb +++ b/lib/hashdiff/diff.rb @@ -9,7 +9,7 @@ module Hashdiff # @param [Array, Hash] obj2 # @param [Hash] options the options to use when comparing # * :strict (Boolean) [true] whether numeric values will be compared on type as well as value. Set to false to allow comparing Integer, Float, BigDecimal to each other - # * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) + # * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) in either hash # * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1}) # * :delimiter (String) ['.'] the delimiter used when returning nested key references # * :numeric_tolerance (Numeric) [0] should be a positive numeric value. Value by which numeric differences must be greater than. By default, numeric values are compared exactly; with the :tolerance option, the difference between numeric values must be greater than the given value. @@ -54,7 +54,7 @@ def self.best_diff(obj1, obj2, options = {}, &block) # @param [Array, Hash] obj2 # @param [Hash] options the options to use when comparing # * :strict (Boolean) [true] whether numeric values will be compared on type as well as value. Set to false to allow comparing Integer, Float, BigDecimal to each other - # * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) + # * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) in either hash # * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1}) # * :similarity (Numeric) [0.8] should be between (0, 1]. Meaningful if there are similar hashes in arrays. See {best_diff}. # * :delimiter (String) ['.'] the delimiter used when returning nested key references @@ -93,7 +93,7 @@ def self.diff(obj1, obj2, options = {}, &block) opts[:prefix] = [] if opts[:array_path] && opts[:prefix] == '' - opts[:ignore_keys] = [*opts[:ignore_keys]] # splat covers single sym/string case + opts[:ignore_keys] = [*opts[:ignore_keys]] opts[:comparison] = block if block_given? diff --git a/spec/hashdiff/diff_spec.rb b/spec/hashdiff/diff_spec.rb index 1bfeb4a..00cfa57 100644 --- a/spec/hashdiff/diff_spec.rb +++ b/spec/hashdiff/diff_spec.rb @@ -50,17 +50,37 @@ end context 'with the ignore_keys option' do - a = { a: 1, b: { d: 2, a: 3 }, c: 4 } - b = { a: 2, b: { d: 2, a: 7 }, c: 5 } + a = { a: 4, g: 0, b: { a: 5, c: 6, e: 1 } } + b = { b: { a: 7, c: 3, f: 1 }, d: 8 } - it 'ignores a single key' do + it 'ignores a single key at first level' do + diff = described_class.diff(a, b, ignore_keys: :d) + diff.should == [['-', 'a', 4], ['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.a', 5, 7], ['~', 'b.c', 6, 3], ['+', 'b.f', 1]] + end + + it 'ignores a single key in nested hash' do + diff = described_class.diff(a, b, ignore_keys: :e) + diff.should == [['-', 'a', 4], ['-', 'g', 0], ['~', 'b.a', 5, 7], ['~', 'b.c', 6, 3], ['+', 'b.f', 1], ['+', 'd', 8]] + end + + it 'ignores a single key at all levels' do diff = described_class.diff(a, b, ignore_keys: :a) - diff.should == [['~', 'c', 4, 5]] + diff.should == [['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'b.f', 1], ['+', 'd', 8]] end - it 'ignores an array of keys' do - diff = described_class.diff(a, b, ignore_keys: %i[a c]) - diff.should == [] + it 'ignores an array of keys at first level' do + diff = described_class.diff(a, b, ignore_keys: %i[g b]) + diff.should == [['-', 'a', 4], ['+', 'd', 8]] + end + + it 'ignores an array of keys in nested hash' do + diff = described_class.diff(a, b, ignore_keys: %i[c e]) + diff.should == [['-', 'a', 4], ['-', 'g', 0], ['~', 'b.a', 5, 7], ['+', 'b.f', 1], ['+', 'd', 8]] + end + + it 'ignores an array of keys at all levels' do + diff = described_class.diff(a, b, ignore_keys: %i[a f]) + diff.should == [['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'd', 8]] end end