Skip to content

Commit c08bee2

Browse files
Ignore equal elements at the ends of sequences in diffseq()
This fixes the two test cases with non-optimal results.
1 parent c61a2a4 commit c08bee2

File tree

2 files changed

+52
-42
lines changed

2 files changed

+52
-42
lines changed

json-diff.js

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -88,45 +88,58 @@ var json_diff = (function() {
8888
var i, j, m, n,
8989
row = [], c = [],
9090
left, diag, latch;
91+
var skip, edit = [];
9192

9293
m = a.length;
9394
n = b.length;
9495

95-
//build the c-table
96-
for (j = 0; j < n; row[j++] = 0);
97-
for (i = 0 ; i < m; i++) {
98-
c[i] = row = row.slice();
99-
for (diag = 0, j = 0; j < n; j++, diag = latch) {
100-
latch = row[j];
101-
if (equal(a[i], b[j]))
102-
row[j] = diag + 1;
103-
else {
104-
left = row[j-1] || 0;
105-
if (left > row[j])
106-
row[j] = left;
96+
// ignore equal elements at both ends
97+
for (; m && n; m--, n--)
98+
if (!equal(a[m - 1], b[n - 1]))
99+
break;
100+
for (skip = 0; m && n; skip++, m--, n--)
101+
if (!equal(a[skip], b[skip]))
102+
break;
103+
104+
if (m && n) {
105+
//build the c-table
106+
for (j = 0; j < n; row[j++] = 0);
107+
for (i = 0 ; i < m; i++) {
108+
c[i] = row = row.slice();
109+
for (diag = 0, j = 0; j < n; j++, diag = latch) {
110+
latch = row[j];
111+
if (equal(a[i + skip], b[j + skip]))
112+
row[j] = diag + 1;
113+
else {
114+
left = row[j - 1] || 0;
115+
if (left > row[j])
116+
row[j] = left;
117+
}
107118
}
108119
}
109-
}
110-
i--, j--;
111-
112-
//row[j] now contains the length of the lcs
113-
//compute the edit sequence from the table
114-
var edit = [];
115-
while (i > -1 && j > -1) {
116-
switch (c[i][j]) {
117-
default:
118-
edit.unshift('=');
119-
i--; j--;
120-
break;
121-
case j && c[i][j - 1]:
122-
edit.unshift('+');
123-
j--;
124-
break;
125-
case i && c[i - 1][j]:
126-
edit.unshift('-');
127-
i--;
128-
break;
120+
i--, j--;
121+
122+
//row[j] now contains the length of the lcs
123+
//compute the edit sequence from the table
124+
while (i > -1 && j > -1) {
125+
switch (c[i][j]) {
126+
default:
127+
edit.unshift('=');
128+
i--; j--;
129+
break;
130+
case j && c[i][j - 1]:
131+
edit.unshift('+');
132+
j--;
133+
break;
134+
case i && c[i - 1][j]:
135+
edit.unshift('-');
136+
i--;
137+
break;
138+
}
129139
}
140+
} else {
141+
i = m - 1;
142+
j = n - 1;
130143
}
131144
for (; j > -1; j--)
132145
edit.unshift('+');
@@ -150,23 +163,23 @@ var json_diff = (function() {
150163
} else
151164
edit_replace.push(edit[n]);
152165
}
153-
// console.log('>>> ' + edit.join('') + ' ' + edit_replace.join(''));
166+
// console.log('>>> ' + skip + ' ' + edit.join('') + ' ' + edit_replace.join(''));
154167
edit = edit_replace;
155168

156169
for (i = 0, j = 0, n = 0; n < edit.length; n++) {
157170
switch(edit[n]) {
158171
case '*':
159-
diff_recursive(a[i], b[j], j);
172+
diff_recursive(a[i + skip], b[j + skip], j + skip);
160173
case '=':
161174
i++;
162175
j++;
163176
break;
164177
case '-':
165-
result('remove', j);
178+
result('remove', j + skip);
166179
i++;
167180
break;
168181
case '+':
169-
result('add', j, b[j]);
182+
result('add', j + skip, b[j + skip]);
170183
j++;
171184
break;
172185
}

test.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ expect([1, 2, 3], [1, 4, 3], [{op: 'replace', path: '/1', value: 4}])
6060
expect({a: [9, 8, 7], b: 2, c: 3}, {a: [9, 7], b: 2, c: 4, d: 5},
6161
[{op: 'remove', path: '/a/1'}, {op: 'replace', path: '/c', value: 4}, {op: 'add', path: '/d', value: 5}])
6262

63+
expect([1, 0, 0], [1, 1, 0], [{op: 'replace', path: '/1', value: 1}]);
64+
expect([1, 1, 0], [1, 0, 0], [{op: 'replace', path: '/1', value: 0}]);
65+
6366
// Reverse examples from RFC 6902, as far as applicable:
6467
expect({foo: 'bar'}, {baz: 'qux', foo: 'bar'}, [{op: 'add', path: '/baz', value: 'qux'}])
6568
expect({foo: ['bar', 'baz']}, {foo: ['bar', 'qux', 'baz']}, [{op: 'add', path: '/foo/1', value: 'qux'}])
@@ -69,12 +72,6 @@ expect({baz: 'qux', foo: 'bar'}, {baz: 'boo', foo: 'bar'}, [{op: 'replace', path
6972
expect({foo: 'bar'}, {foo: 'bar', child: {grandchild: {}}}, [{op: 'add', path: '/child', value: {'grandchild': {}}}])
7073
expect({foo: ['bar']}, {foo: ['bar', ['abc', 'def']]}, [{op: 'add', path: '/foo/1', value: ['abc', 'def']}])
7174

72-
// The following cases produce non-optimal diffs:
73-
expect([1, 0, 0], [1, 1, 0], [{op: 'add', path: '/1', value: 1}, {op: 'remove', path: '/3'}]);
74-
// ideal: [{op: 'replace', path: '/1', value: 1}]
75-
expect([1, 1, 0], [1, 0, 0], [{op: 'remove', path: '/1'}, {op: 'add', path: '/2', value: 0}]);
76-
// ideal: [{op: 'replace', path: '/1', value: 0}]
77-
7875
if (failures) {
7976
console.log(failures + ' FAILURE(S)');
8077
process.exit(1);

0 commit comments

Comments
 (0)