immutablediff
Version:
Create RFC 6902 style patches between Immutable.JS data structures
147 lines (131 loc) • 3.56 kB
JavaScript
'use strict';
var Immutable = require('immutable');
/**
* Returns a two-dimensional array (an array of arrays) with dimensions n by m.
* All the elements of this new matrix are initially equal to x
* @param n number of rows
* @param m number of columns
* @param x initial element for every item in matrix
*/
var makeMatrix = function(n, m, x){
var matrix = [];
for(var i = 0; i < n; i++) {
matrix[i] = new Array(m);
if(x != null){
for(var j = 0; j < m; j++){
matrix[i][j] = x;
}
}
}
return matrix;
};
/**
* Computes Longest Common Subsequence between two Immutable.JS Indexed Iterables
* Based on Dynamic Programming http://rosettacode.org/wiki/Longest_common_subsequence#Java
* @param xs ImmutableJS Indexed Sequence 1
* @param ys ImmutableJS Indexed Sequence 2
*/
var lcs = function(xs, ys){
var matrix = computeLcsMatrix(xs, ys);
return backtrackLcs(xs, ys, matrix);
};
var DiffResult = Immutable.Record({op: '=', val: null});
var ReplaceResult = Immutable.Record({op: '!=', val: null, newVal: null});
/**
* Returns the resulting diff operations of LCS between two sequences
* @param xs Indexed Sequence 1
* @param ys Indexed Sequence 2
* @returns Array of DiffResult {op:'=' | '+' | '-', val:any}
*/
var diff = function(xs, ys){
var matrix = computeLcsMatrix(xs, ys);
return printDiff(matrix, xs, ys, xs.size||0, ys.size||0);
};
var printDiff = function(matrix, xs, ys, xSize, ySize) {
var diffArray = [];
var i = xSize - 1;
var j = ySize - 1;
while (i >= 0 || j >= 0) {
if (i >= 0 && j >= 0 && Immutable.is(xs.get(i), ys.get(j))) {
diffArray.push(new DiffResult({
op: '=',
val: xs.get(i)
}));
i -= 1;
j -= 1;
}
else if (i >= 0 && j >= 0 && i === j && !Immutable.is(xs.get(i), ys.get(j))) {
diffArray.push(new ReplaceResult({
val: xs.get(i),
newVal: ys.get(i)
}));
i -= 1;
j -= 1;
}
else {
if (j >= 0 && (i === -1 || matrix[i+1][j] >= matrix[i][j+1])) {
diffArray.push(new DiffResult({
op: '+',
val: ys.get(j)
}));
j -= 1;
}
else if (i >= 0 && (j === -1 || matrix[i+1][j] < matrix[i][j+1])){
diffArray.push(new DiffResult({
op: '-',
val: xs.get(i)
}));
i -= 1;
}
}
}
return diffArray.reverse();
};
/**
* Computes the Longest Common Subsequence table
* @param xs Indexed Sequence 1
* @param ys Indexed Sequence 2
*/
function computeLcsMatrix(xs, ys) {
var n = xs.size||0;
var m = ys.size||0;
var a = makeMatrix(n + 1, m + 1, 0);
for (var i = 0; i < n; i++) {
for (var j = 0; j < m; j++) {
if (Immutable.is(xs.get(i), ys.get(j))) {
a[i + 1][j + 1] = a[i][j] + 1;
}
else {
a[i + 1][j + 1] = Math.max(a[i + 1][j], a[i][j + 1]);
}
}
}
return a;
}
/**
* Extracts a LCS from matrix M
* @param xs Indexed Sequence 1
* @param ys Indexed Sequence 2
* @param matrix LCS Matrix
* @returns {Array.<T>} Longest Common Subsequence
*/
var backtrackLcs = function(xs, ys, matrix){
var lcs = [];
for(var i = xs.size, j = ys.size; i !== 0 && j !== 0;){
if (matrix[i][j] === matrix[i-1][j]){ i--; }
else if (matrix[i][j] === matrix[i][j-1]){ j--; }
else{
if(Immutable.is(xs.get(i-1), ys.get(j-1))){
lcs.push(xs.get(i-1));
i--;
j--;
}
}
}
return lcs.reverse();
};
module.exports = {
lcs: lcs,
computeLcsMatrix: computeLcsMatrix,
diff: diff
};