package.src.core.arrayDiff.ts Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zrender Show documentation
Show all versions of zrender Show documentation
A lightweight graphic library providing 2d draw for Apache ECharts
The newest version!
// Myers' Diff Algorithm
// Modified from https://github.com/kpdecker/jsdiff/blob/master/src/diff/base.js
type EqualFunc = (a: T, b: T) => boolean;
type DiffComponent = {
count: number
added: boolean
removed: boolean,
indices: number[]
}
type DiffPath = {
components: DiffComponent[],
newPos: number
}
// Using O(ND) algorithm
// TODO: Optimize when diff is large.
function diff(oldArr: T[], newArr: T[], equals: EqualFunc): DiffComponent[] {
if (!equals) {
equals = function (a, b) {
return a === b;
};
}
oldArr = oldArr.slice();
newArr = newArr.slice();
// Allow subclasses to massage the input prior to running
var newLen = newArr.length;
var oldLen = oldArr.length;
var editLength = 1;
var maxEditLength = newLen + oldLen;
var bestPath: DiffPath[] = [{ newPos: -1, components: [] }];
// Seed editLength = 0, i.e. the content starts with the same values
var oldPos = extractCommon(bestPath[0], newArr, oldArr, 0, equals);
if (!oldLen // All new created
|| !newLen // Clear
|| (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen)) {
var indices = [];
var allCleared = !newLen && oldLen > 0;
var allCreated = !oldLen && newLen > 0;
for (let i = 0; i < (allCleared ? oldArr : newArr).length; i++) {
indices.push(i);
}
// Identity per the equality and tokenizer
return [{
indices: indices,
count: indices.length,
added: allCreated,
removed: allCleared
}];
}
// Main worker method. checks all permutations of a given edit length for acceptance.
function execEditLength() {
for (let 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) {
// No one else is going to attempt to use this value, clear it
bestPath[diagonalPath - 1] = undefined;
}
var canAdd = addPath && addPath.newPos + 1 < newLen;
var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
if (!canAdd && !canRemove) {
// If this path is a terminal then prune
bestPath[diagonalPath] = undefined;
continue;
}
// Select the diagonal that we want to branch from. We select the prior
// path whose position in the new string is the farthest from the origin
// and does not pass the bounds of the diff graph
if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
basePath = clonePath(removePath);
pushComponent(basePath.components, false, true);
}
else {
basePath = addPath; // No need to clone, we've pulled it from the list
basePath.newPos++;
pushComponent(basePath.components, true, false);
}
oldPos = extractCommon(basePath, newArr, oldArr, diagonalPath, equals);
// If we have hit the end of both strings, then we are done
if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
return buildValues(basePath.components);
}
else {
// Otherwise track this path as a potential candidate and continue.
bestPath[diagonalPath] = basePath;
}
}
editLength++;
}
while (editLength <= maxEditLength) {
var ret = execEditLength();
if (ret) {
return ret;
}
}
}
function extractCommon(basePath: DiffPath, newArr: T[], oldArr: T[], diagonalPath: number, equals: EqualFunc) {
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: DiffComponent[], added: boolean, removed: boolean) {
var last = components[components.length - 1];
if (last && last.added === added && last.removed === removed) {
// We need to clone here as the component clone operation is just
// as shallow array clone
components[components.length - 1] = {
count: last.count + 1,
added,
removed,
indices: []
};
}
else {
components.push({
count: 1,
added,
removed,
indices: []
});
}
}
function buildValues(components: DiffComponent[]) {
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 (let i = newPos; i < newPos + component.count; i++) {
indices.push(i);
}
component.indices = indices;
newPos += component.count;
// Common case
if (!component.added) {
oldPos += component.count;
}
}
else {
for (let i = oldPos; i < oldPos + component.count; i++) {
component.indices.push(i);
}
oldPos += component.count;
}
}
return components;
}
function clonePath(path: DiffPath) {
return { newPos: path.newPos, components: path.components.slice(0) };
}
export default function arrayDiff(oldArr: T[], newArr: T[], equal?: EqualFunc): DiffComponent[] {
return diff(oldArr, newArr, equal);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy