Skip to content

Commit b0f0566

Browse files
authored
Merge pull request #237 from aryelu/master
make backtrack iterative should fix #223
2 parents 69555e3 + 0d54772 commit b0f0566

File tree

2 files changed

+76
-35
lines changed

2 files changed

+76
-35
lines changed

src/filters/lcs.js

+33-35
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ reference: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
77
*/
88

99
const defaultMatch = function(array1, array2, index1, index2) {
10-
return array1[index1] === array2[index2];
10+
return array1[ index1 ] === array2[ index2 ];
1111
};
1212

1313
const lengthMatrix = function(array1, array2, match, context) {
@@ -16,56 +16,56 @@ const lengthMatrix = function(array1, array2, match, context) {
1616
let x, y;
1717

1818
// initialize empty matrix of len1+1 x len2+1
19-
let matrix = [len1 + 1];
19+
let matrix = [ len1 + 1 ];
2020
for (x = 0; x < len1 + 1; x++) {
21-
matrix[x] = [len2 + 1];
21+
matrix[ x ] = [ len2 + 1 ];
2222
for (y = 0; y < len2 + 1; y++) {
23-
matrix[x][y] = 0;
23+
matrix[ x ][ y ] = 0;
2424
}
2525
}
2626
matrix.match = match;
2727
// save sequence lengths for each coordinate
2828
for (x = 1; x < len1 + 1; x++) {
2929
for (y = 1; y < len2 + 1; y++) {
3030
if (match(array1, array2, x - 1, y - 1, context)) {
31-
matrix[x][y] = matrix[x - 1][y - 1] + 1;
31+
matrix[ x ][ y ] = matrix[ x - 1 ][ y - 1 ] + 1;
3232
} else {
33-
matrix[x][y] = Math.max(matrix[x - 1][y], matrix[x][y - 1]);
33+
matrix[ x ][ y ] = Math.max(matrix[ x - 1 ][ y ], matrix[ x ][ y - 1 ]);
3434
}
3535
}
3636
}
3737
return matrix;
3838
};
3939

40-
const backtrack = function(matrix, array1, array2, index1, index2, context) {
41-
if (index1 === 0 || index2 === 0) {
42-
return {
43-
sequence: [],
44-
indices1: [],
45-
indices2: [],
46-
};
47-
}
48-
49-
if (matrix.match(array1, array2, index1 - 1, index2 - 1, context)) {
50-
const subsequence = backtrack(
51-
matrix,
52-
array1,
53-
array2,
54-
index1 - 1,
55-
index2 - 1,
56-
context
57-
);
58-
subsequence.sequence.push(array1[index1 - 1]);
59-
subsequence.indices1.push(index1 - 1);
60-
subsequence.indices2.push(index2 - 1);
61-
return subsequence;
62-
}
40+
const backtrack = function(matrix, array1, array2, context) {
41+
let index1 = array1.length;
42+
let index2 = array2.length;
43+
const subsequence = {
44+
sequence: [],
45+
indices1: [],
46+
indices2: [],
47+
};
6348

64-
if (matrix[index1][index2 - 1] > matrix[index1 - 1][index2]) {
65-
return backtrack(matrix, array1, array2, index1, index2 - 1, context);
66-
} else {
67-
return backtrack(matrix, array1, array2, index1 - 1, index2, context);
49+
while (index1 !== 0 && index2 !== 0) {
50+
const sameLetter =
51+
matrix.match(array1, array2, index1 - 1, index2 - 1, context);
52+
if (sameLetter) {
53+
subsequence.sequence.unshift(array1[ index1 - 1 ]);
54+
subsequence.indices1.unshift(index1 - 1);
55+
subsequence.indices2.unshift(index2 - 1);
56+
--index1;
57+
--index2;
58+
} else {
59+
const valueAtMatrixAbove = matrix[ index1 ][ index2 - 1 ];
60+
const valueAtMatrixLeft = matrix[ index1 - 1 ][ index2 ];
61+
if (valueAtMatrixAbove > valueAtMatrixLeft) {
62+
--index2;
63+
} else {
64+
--index1;
65+
}
66+
}
6867
}
68+
return subsequence;
6969
};
7070

7171
const get = function(array1, array2, match, context) {
@@ -80,8 +80,6 @@ const get = function(array1, array2, match, context) {
8080
matrix,
8181
array1,
8282
array2,
83-
array1.length,
84-
array2.length,
8583
innerContext
8684
);
8785
if (typeof array1 === 'string' && typeof array2 === 'string') {

test/index.js

+43
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import * as jsondiffpatch from '../build/jsondiffpatch.esm';
66
import examples from './examples/diffpatch';
77
import chai from 'chai';
8+
9+
import lcs from '../src/filters/lcs';
810
const expect = chai.expect;
911

1012
describe('jsondiffpatch', () => {
@@ -726,3 +728,44 @@ describe('DiffPatcher', () => {
726728
});
727729
});
728730
});
731+
732+
describe('lcs', () => {
733+
it('should lcs arrays ', () => {
734+
expect(lcs.get([], [])).to.deep.equal({
735+
sequence: [],
736+
indices1: [],
737+
indices2: [],
738+
});
739+
740+
expect(lcs.get([1], [2])).to.deep.equal({
741+
sequence: [],
742+
indices1: [],
743+
indices2: [],
744+
});
745+
746+
// indices1 and indices2 show where the sequence
747+
// elements are located in the original arrays
748+
expect(lcs.get([ 1 ], [ -9, 1 ])).to.deep.equal({
749+
sequence: [1],
750+
indices1: [0],
751+
indices2: [1],
752+
});
753+
});
754+
755+
it('should compute diff for large array', () => {
756+
const ARRAY_LENGTH = 5000; // js stack is about 50k
757+
function randomArray() {
758+
let result = [];
759+
for (let i = 0; i < ARRAY_LENGTH; i++) {
760+
if (Math.random() > 0.5) {
761+
result.push('A');
762+
} else {
763+
result.push('B');
764+
}
765+
}
766+
return result;
767+
}
768+
769+
lcs.get(randomArray(), randomArray());
770+
});
771+
});

0 commit comments

Comments
 (0)