146 lines
4.6 KiB
JavaScript
146 lines
4.6 KiB
JavaScript
|
function diff(oldArr, newArr, equals) {
|
||
|
if (!equals) {
|
||
|
equals = function (a, b) {
|
||
|
return a === b;
|
||
|
};
|
||
|
}
|
||
|
oldArr = oldArr.slice();
|
||
|
newArr = newArr.slice();
|
||
|
var newLen = newArr.length;
|
||
|
var oldLen = oldArr.length;
|
||
|
var editLength = 1;
|
||
|
var maxEditLength = newLen + oldLen;
|
||
|
var bestPath = [{ newPos: -1, components: [] }];
|
||
|
var oldPos = extractCommon(bestPath[0], newArr, oldArr, 0, equals);
|
||
|
if (!oldLen
|
||
|
|| !newLen
|
||
|
|| (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen)) {
|
||
|
var indices = [];
|
||
|
var allCleared = !newLen && oldLen > 0;
|
||
|
var allCreated = !oldLen && newLen > 0;
|
||
|
for (var i = 0; i < (allCleared ? oldArr : newArr).length; i++) {
|
||
|
indices.push(i);
|
||
|
}
|
||
|
return [{
|
||
|
indices: indices,
|
||
|
count: indices.length,
|
||
|
added: allCreated,
|
||
|
removed: allCleared
|
||
|
}];
|
||
|
}
|
||
|
function execEditLength() {
|
||
|
for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
|
||
|
var basePath;
|
||
|
var addPath = bestPath[diagonalPath - 1];
|
||
|
var removePath = bestPath[diagonalPath + 1];
|
||
|
var oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
|
||
|
if (addPath) {
|
||
|
bestPath[diagonalPath - 1] = undefined;
|
||
|
}
|
||
|
var canAdd = addPath && addPath.newPos + 1 < newLen;
|
||
|
var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
|
||
|
if (!canAdd && !canRemove) {
|
||
|
bestPath[diagonalPath] = undefined;
|
||
|
continue;
|
||
|
}
|
||
|
if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
|
||
|
basePath = clonePath(removePath);
|
||
|
pushComponent(basePath.components, false, true);
|
||
|
}
|
||
|
else {
|
||
|
basePath = addPath;
|
||
|
basePath.newPos++;
|
||
|
pushComponent(basePath.components, true, false);
|
||
|
}
|
||
|
oldPos = extractCommon(basePath, newArr, oldArr, diagonalPath, equals);
|
||
|
if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
|
||
|
return buildValues(basePath.components);
|
||
|
}
|
||
|
else {
|
||
|
bestPath[diagonalPath] = basePath;
|
||
|
}
|
||
|
}
|
||
|
editLength++;
|
||
|
}
|
||
|
while (editLength <= maxEditLength) {
|
||
|
var ret = execEditLength();
|
||
|
if (ret) {
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function extractCommon(basePath, newArr, oldArr, diagonalPath, equals) {
|
||
|
var newLen = newArr.length;
|
||
|
var oldLen = oldArr.length;
|
||
|
var newPos = basePath.newPos;
|
||
|
var oldPos = newPos - diagonalPath;
|
||
|
var commonCount = 0;
|
||
|
while (newPos + 1 < newLen && oldPos + 1 < oldLen && equals(newArr[newPos + 1], oldArr[oldPos + 1])) {
|
||
|
newPos++;
|
||
|
oldPos++;
|
||
|
commonCount++;
|
||
|
}
|
||
|
if (commonCount) {
|
||
|
basePath.components.push({
|
||
|
count: commonCount,
|
||
|
added: false,
|
||
|
removed: false,
|
||
|
indices: []
|
||
|
});
|
||
|
}
|
||
|
basePath.newPos = newPos;
|
||
|
return oldPos;
|
||
|
}
|
||
|
function pushComponent(components, added, removed) {
|
||
|
var last = components[components.length - 1];
|
||
|
if (last && last.added === added && last.removed === removed) {
|
||
|
components[components.length - 1] = {
|
||
|
count: last.count + 1,
|
||
|
added: added,
|
||
|
removed: removed,
|
||
|
indices: []
|
||
|
};
|
||
|
}
|
||
|
else {
|
||
|
components.push({
|
||
|
count: 1,
|
||
|
added: added,
|
||
|
removed: removed,
|
||
|
indices: []
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
function buildValues(components) {
|
||
|
var componentPos = 0;
|
||
|
var componentLen = components.length;
|
||
|
var newPos = 0;
|
||
|
var oldPos = 0;
|
||
|
for (; componentPos < componentLen; componentPos++) {
|
||
|
var component = components[componentPos];
|
||
|
if (!component.removed) {
|
||
|
var indices = [];
|
||
|
for (var i = newPos; i < newPos + component.count; i++) {
|
||
|
indices.push(i);
|
||
|
}
|
||
|
component.indices = indices;
|
||
|
newPos += component.count;
|
||
|
if (!component.added) {
|
||
|
oldPos += component.count;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (var i = oldPos; i < oldPos + component.count; i++) {
|
||
|
component.indices.push(i);
|
||
|
}
|
||
|
oldPos += component.count;
|
||
|
}
|
||
|
}
|
||
|
return components;
|
||
|
}
|
||
|
function clonePath(path) {
|
||
|
return { newPos: path.newPos, components: path.components.slice(0) };
|
||
|
}
|
||
|
export default function arrayDiff(oldArr, newArr, equal) {
|
||
|
return diff(oldArr, newArr, equal);
|
||
|
}
|