Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #88 bug in ignore_keys option #93

Merged
merged 1 commit into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
6 changes: 5 additions & 1 deletion lib/hashdiff/compare_hashes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
6 changes: 3 additions & 3 deletions lib/hashdiff/diff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?

Expand Down
34 changes: 27 additions & 7 deletions spec/hashdiff/diff_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down