hashdiff 0.3.8 → 0.3.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile +2 -0
- data/README.md +3 -1
- data/Rakefile +2 -0
- data/changelog.md +4 -1
- data/hashdiff.gemspec +3 -1
- data/lib/hashdiff.rb +8 -6
- data/lib/hashdiff/diff.rb +22 -13
- data/lib/hashdiff/lcs.rb +4 -2
- data/lib/hashdiff/linear_compare_array.rb +2 -0
- data/lib/hashdiff/patch.rb +2 -0
- data/lib/hashdiff/util.rb +9 -8
- data/lib/hashdiff/version.rb +3 -1
- data/spec/hash_diff/best_diff_spec.rb +2 -0
- data/spec/hash_diff/diff_array_spec.rb +2 -0
- data/spec/hash_diff/diff_spec.rb +2 -0
- data/spec/hash_diff/lcs_spec.rb +2 -0
- data/spec/hash_diff/linear_compare_array_spec.rb +2 -0
- data/spec/hash_diff/patch_spec.rb +2 -0
- data/spec/hash_diff/util_spec.rb +10 -0
- data/spec/spec_helper.rb +2 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1de380f239260b12890d36de1548ee8a9159a15d
|
4
|
+
data.tar.gz: be13f7000b31ac211e59c8e5c924a976a39fb26c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bad33e3fd08a4f7b14388f54c17060153a48832eda6c06e6d137ce23b901cd14183630eabcfa7e1da6d2c93f33d7b59109a7fee0e78a23f2a3102e3576c35846
|
7
|
+
data.tar.gz: 7fa406149636c91d9cc5abc49b926f92e8b4962654e9b78ef18b3938dc971ce2f51cf16b2c01b3968556cc20fbfb577437b76a7094ecf0ba4eb5cd00cdba0bb3
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,6 +8,9 @@ HashDiff does not monkey-patch any existing class. All features are contained in
|
|
8
8
|
|
9
9
|
**Docs**: [Documentation](http://rubydoc.info/gems/hashdiff)
|
10
10
|
|
11
|
+
|
12
|
+
__WARNING__: Don't use the library for comparing large arrays, say ~10K (see #49).
|
13
|
+
|
11
14
|
## Why HashDiff?
|
12
15
|
|
13
16
|
Given two Hashes A and B, sometimes you face the question: what's the smallest modification that can be made to change A into B?
|
@@ -262,4 +265,3 @@ HashDiff.diff(a, b) => []
|
|
262
265
|
## License
|
263
266
|
|
264
267
|
HashDiff is distributed under the MIT-LICENSE.
|
265
|
-
|
data/Rakefile
CHANGED
data/changelog.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## v0.3.9 2019-04-22
|
4
|
+
|
5
|
+
* Performance tweak (thanks @krzysiek1507: #51 #52 #53)
|
6
|
+
|
3
7
|
## v0.3.8 2018-12-30
|
4
8
|
|
5
9
|
* Add Rubocop and drops Ruby 1.9 support #47
|
@@ -73,4 +77,3 @@ For example, `diff({a:2, c:[4, 5]}, {a:2}) will generate following output:
|
|
73
77
|
instead of following:
|
74
78
|
|
75
79
|
[['-', 'c[0]', 4], ['-', 'c[1]', 5], ['-', 'c', []]]
|
76
|
-
|
data/hashdiff.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
$LOAD_PATH << File.expand_path('lib', __dir__)
|
2
4
|
require 'hashdiff/version'
|
3
5
|
|
@@ -12,7 +14,7 @@ Gem::Specification.new do |s|
|
|
12
14
|
s.test_files = `git ls-files -- Appraisals {spec}/*`.split("\n")
|
13
15
|
|
14
16
|
s.require_paths = ['lib']
|
15
|
-
s.required_ruby_version = Gem::Requirement.new('>=
|
17
|
+
s.required_ruby_version = Gem::Requirement.new('>= 2.0.0')
|
16
18
|
|
17
19
|
s.authors = ['Liu Fengyun']
|
18
20
|
s.email = ['liufengyunchina@gmail.com']
|
data/lib/hashdiff.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'hashdiff/util'
|
4
|
+
require_relative 'hashdiff/lcs'
|
5
|
+
require_relative 'hashdiff/linear_compare_array'
|
6
|
+
require_relative 'hashdiff/diff'
|
7
|
+
require_relative 'hashdiff/patch'
|
8
|
+
require_relative 'hashdiff/version'
|
data/lib/hashdiff/diff.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HashDiff
|
2
4
|
# Best diff two objects, which tries to generate the smallest change set using different similarity values.
|
3
5
|
#
|
@@ -120,13 +122,15 @@ module HashDiff
|
|
120
122
|
elsif obj1.is_a?(Array) && !opts[:use_lcs]
|
121
123
|
result.concat(LinearCompareArray.call(obj1, obj2, opts))
|
122
124
|
elsif obj1.is_a?(Hash)
|
125
|
+
obj1_keys = obj1.keys
|
126
|
+
obj2_keys = obj2.keys
|
123
127
|
|
124
|
-
deleted_keys =
|
125
|
-
common_keys =
|
126
|
-
added_keys =
|
128
|
+
deleted_keys = (obj1_keys - obj2_keys).sort_by(&:to_s)
|
129
|
+
common_keys = (obj1_keys & obj2_keys).sort_by(&:to_s)
|
130
|
+
added_keys = (obj2_keys - obj1_keys).sort_by(&:to_s)
|
127
131
|
|
128
132
|
# add deleted properties
|
129
|
-
deleted_keys.
|
133
|
+
deleted_keys.each do |k|
|
130
134
|
change_key = prefix_append_key(opts[:prefix], k, opts)
|
131
135
|
custom_result = custom_compare(opts[:comparison], change_key, obj1[k], nil)
|
132
136
|
|
@@ -138,13 +142,13 @@ module HashDiff
|
|
138
142
|
end
|
139
143
|
|
140
144
|
# recursive comparison for common keys
|
141
|
-
common_keys.
|
145
|
+
common_keys.each do |k|
|
142
146
|
prefix = prefix_append_key(opts[:prefix], k, opts)
|
143
147
|
result.concat(diff(obj1[k], obj2[k], opts.merge(prefix: prefix)))
|
144
148
|
end
|
145
149
|
|
146
150
|
# added properties
|
147
|
-
added_keys.
|
151
|
+
added_keys.each do |k|
|
148
152
|
change_key = prefix_append_key(opts[:prefix], k, opts)
|
149
153
|
next if obj1.key?(k)
|
150
154
|
|
@@ -169,28 +173,33 @@ module HashDiff
|
|
169
173
|
#
|
170
174
|
# diff array using LCS algorithm
|
171
175
|
def self.diff_array_lcs(arraya, arrayb, options = {})
|
172
|
-
|
173
|
-
prefix: '',
|
174
|
-
similarity: 0.8,
|
175
|
-
delimiter: '.'
|
176
|
-
}.merge!(options)
|
176
|
+
return [] if arraya.empty? && arrayb.empty?
|
177
177
|
|
178
178
|
change_set = []
|
179
|
-
return [] if arraya.empty? && arrayb.empty?
|
180
179
|
|
181
180
|
if arraya.empty?
|
182
181
|
arrayb.each_index do |index|
|
183
182
|
change_set << ['+', index, arrayb[index]]
|
184
183
|
end
|
184
|
+
|
185
185
|
return change_set
|
186
|
-
|
186
|
+
end
|
187
|
+
|
188
|
+
if arrayb.empty?
|
187
189
|
arraya.each_index do |index|
|
188
190
|
i = arraya.size - index - 1
|
189
191
|
change_set << ['-', i, arraya[i]]
|
190
192
|
end
|
193
|
+
|
191
194
|
return change_set
|
192
195
|
end
|
193
196
|
|
197
|
+
opts = {
|
198
|
+
prefix: '',
|
199
|
+
similarity: 0.8,
|
200
|
+
delimiter: '.'
|
201
|
+
}.merge!(options)
|
202
|
+
|
194
203
|
links = lcs(arraya, arrayb, opts)
|
195
204
|
|
196
205
|
# yield common
|
data/lib/hashdiff/lcs.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HashDiff
|
2
4
|
# @private
|
3
5
|
#
|
4
6
|
# caculate array difference using LCS algorithm
|
5
7
|
# http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
|
6
8
|
def self.lcs(arraya, arrayb, options = {})
|
9
|
+
return [] if arraya.empty? || arrayb.empty?
|
10
|
+
|
7
11
|
opts = { similarity: 0.8 }.merge!(options)
|
8
12
|
|
9
13
|
opts[:prefix] = prefix_append_array_index(opts[:prefix], '*', opts)
|
10
14
|
|
11
|
-
return [] if arraya.empty? || arrayb.empty?
|
12
|
-
|
13
15
|
a_start = b_start = 0
|
14
16
|
a_finish = arraya.size - 1
|
15
17
|
b_finish = arrayb.size - 1
|
data/lib/hashdiff/patch.rb
CHANGED
data/lib/hashdiff/util.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HashDiff
|
2
4
|
# @private
|
3
5
|
#
|
@@ -5,14 +7,15 @@ module HashDiff
|
|
5
7
|
def self.similar?(obja, objb, options = {})
|
6
8
|
return compare_values(obja, objb, options) unless obja.is_a?(Array) || obja.is_a?(Hash) || objb.is_a?(Array) || objb.is_a?(Hash)
|
7
9
|
|
8
|
-
opts = { similarity: 0.8 }.merge(options)
|
9
|
-
|
10
10
|
count_a = count_nodes(obja)
|
11
11
|
count_b = count_nodes(objb)
|
12
|
-
diffs = count_diff diff(obja, objb, opts)
|
13
12
|
|
14
13
|
return true if (count_a + count_b).zero?
|
15
14
|
|
15
|
+
opts = { similarity: 0.8 }.merge!(options)
|
16
|
+
|
17
|
+
diffs = count_diff diff(obja, objb, opts)
|
18
|
+
|
16
19
|
(1 - diffs.to_f / (count_a + count_b).to_f) >= opts[:similarity]
|
17
20
|
end
|
18
21
|
|
@@ -81,8 +84,8 @@ module HashDiff
|
|
81
84
|
#
|
82
85
|
# check for equality or "closeness" within given tolerance
|
83
86
|
def self.compare_values(obj1, obj2, options = {})
|
84
|
-
if
|
85
|
-
|
87
|
+
if options[:numeric_tolerance].is_a?(Numeric) &&
|
88
|
+
obj1.is_a?(Numeric) && obj2.is_a?(Numeric)
|
86
89
|
return (obj1 - obj2).abs <= options[:numeric_tolerance]
|
87
90
|
end
|
88
91
|
|
@@ -103,9 +106,7 @@ module HashDiff
|
|
103
106
|
#
|
104
107
|
# check if objects are comparable
|
105
108
|
def self.comparable?(obj1, obj2, strict = true)
|
106
|
-
|
107
|
-
return true if obj1.is_a?(type) && obj2.is_a?(type)
|
108
|
-
end
|
109
|
+
return true if (obj1.is_a?(Array) || obj1.is_a?(Hash)) && obj2.is_a?(obj1.class)
|
109
110
|
return true if !strict && obj1.is_a?(Numeric) && obj2.is_a?(Numeric)
|
110
111
|
|
111
112
|
obj1.is_a?(obj2.class) && obj2.is_a?(obj1.class)
|
data/lib/hashdiff/version.rb
CHANGED
data/spec/hash_diff/diff_spec.rb
CHANGED
data/spec/hash_diff/lcs_spec.rb
CHANGED
data/spec/hash_diff/util_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe HashDiff do
|
@@ -18,6 +20,14 @@ describe HashDiff do
|
|
18
20
|
described_class.similar?(a, b, similarity: 1).should be false
|
19
21
|
end
|
20
22
|
|
23
|
+
it 'is able to tell similiar empty hash' do
|
24
|
+
described_class.similar?({}, {}, 1).should be true
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is able to tell similiar empty array' do
|
28
|
+
described_class.similar?([], [], 1).should be true
|
29
|
+
end
|
30
|
+
|
21
31
|
it 'is able to tell similiar hash with values within tolerance' do
|
22
32
|
a = { 'a' => 1.5, 'b' => 2.25, 'c' => 3, 'd' => 4, 'e' => 5 }
|
23
33
|
b = { 'a' => 1.503, 'b' => 2.22, 'c' => 3, 'e' => 5 }
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hashdiff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Liu Fengyun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bluecloth
|
@@ -126,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
126
|
requirements:
|
127
127
|
- - ">="
|
128
128
|
- !ruby/object:Gem::Version
|
129
|
-
version:
|
129
|
+
version: 2.0.0
|
130
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
131
|
requirements:
|
132
132
|
- - ">="
|