Superdiff provides a complete and readable diff for both arrays and objects. Plus, it supports stream and file inputs for handling large datasets efficiently, is battle-tested, has zero dependencies, and is super fast.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
2.8 KiB

import {
LIST_STATUS,
ListDiff,
ListData,
ListDiffStatus,
ListOptions,
} from "./model";
2 years ago
import { isEqual } from "./utils";
function getLeanDiff(
diff: ListDiff["diff"],
showOnly = [] as ListOptions["showOnly"]
): ListDiff["diff"] {
return diff.filter((value) => showOnly?.includes(value.status));
}
2 years ago
function formatSingleListDiff(
listData: ListData[],
status: ListDiffStatus
2 years ago
): ListDiff {
return {
type: "list",
status,
diff: listData.map((data: ListData, i) => ({
value: data,
prevIndex: status === LIST_STATUS.ADDED ? null : i,
newIndex: status === LIST_STATUS.ADDED ? i : null,
indexDiff: null,
status,
})),
2 years ago
};
}
function getListStatus(listDiff: ListDiff["diff"]): ListDiffStatus {
return listDiff.some((value) => value.status !== LIST_STATUS.EQUAL)
? LIST_STATUS.UPDATED
: LIST_STATUS.EQUAL;
}
2 years ago
export const getListDiff = (
prevList: ListData[] | undefined | null,
nextList: ListData[] | undefined | null,
options: ListOptions = { showOnly: [] }
2 years ago
): ListDiff => {
if (!prevList && !nextList) {
return {
type: "list",
status: LIST_STATUS.EQUAL,
2 years ago
diff: [],
};
}
if (!prevList) {
return formatSingleListDiff(nextList as ListData, LIST_STATUS.ADDED);
2 years ago
}
if (!nextList) {
return formatSingleListDiff(prevList as ListData, LIST_STATUS.DELETED);
2 years ago
}
const diff: ListDiff["diff"] = [];
const prevIndexMatches: number[] = [];
2 years ago
nextList.forEach((nextValue, i) => {
const prevIndex = prevList.findIndex(
(prevValue, prevIdx) =>
isEqual(prevValue, nextValue) && !prevIndexMatches.includes(prevIdx)
2 years ago
);
if (prevIndex > -1) {
prevIndexMatches.push(prevIndex);
}
2 years ago
const indexDiff = prevIndex === -1 ? null : i - prevIndex;
if (indexDiff === 0) {
return diff.push({
value: nextValue,
prevIndex,
newIndex: i,
indexDiff,
status: LIST_STATUS.EQUAL,
2 years ago
});
}
if (prevIndex === -1) {
return diff.push({
value: nextValue,
prevIndex: null,
newIndex: i,
indexDiff,
status: LIST_STATUS.ADDED,
2 years ago
});
}
return diff.push({
value: nextValue,
prevIndex,
newIndex: i,
indexDiff,
status: LIST_STATUS.MOVED,
2 years ago
});
});
prevList.forEach((prevValue, i) => {
if (!prevIndexMatches.includes(i)) {
2 years ago
return diff.splice(i, 0, {
value: prevValue,
prevIndex: i,
newIndex: null,
indexDiff: null,
status: LIST_STATUS.DELETED,
2 years ago
});
}
});
if (options.showOnly && options?.showOnly?.length > 0) {
return {
type: "list",
status: getListStatus(diff),
diff: getLeanDiff(diff, options.showOnly),
};
}
2 years ago
return {
type: "list",
status: getListStatus(diff),
2 years ago
diff,
};
};