97 lines
2.4 KiB
JavaScript
97 lines
2.4 KiB
JavaScript
export const STATUS_ADD = 'add';
|
|
export const STATUS_KEEP = 'keep';
|
|
export const STATUS_REMOVE = 'remove';
|
|
export const STATUS_REMOVED = 'removed';
|
|
export function wrapKeyToObject(key) {
|
|
let keyObj;
|
|
if (key && typeof key === 'object' && 'key' in key) {
|
|
keyObj = key;
|
|
} else {
|
|
keyObj = {
|
|
key: key
|
|
};
|
|
}
|
|
return {
|
|
...keyObj,
|
|
key: String(keyObj.key)
|
|
};
|
|
}
|
|
export function parseKeys(keys = []) {
|
|
return keys.map(wrapKeyToObject);
|
|
}
|
|
export function diffKeys(prevKeys = [], currentKeys = []) {
|
|
let list = [];
|
|
let currentIndex = 0;
|
|
const currentLen = currentKeys.length;
|
|
const prevKeyObjects = parseKeys(prevKeys);
|
|
const currentKeyObjects = parseKeys(currentKeys);
|
|
|
|
// Check prev keys to insert or keep
|
|
prevKeyObjects.forEach(keyObj => {
|
|
let hit = false;
|
|
for (let i = currentIndex; i < currentLen; i += 1) {
|
|
const currentKeyObj = currentKeyObjects[i];
|
|
if (currentKeyObj.key === keyObj.key) {
|
|
// New added keys should add before current key
|
|
if (currentIndex < i) {
|
|
list = list.concat(currentKeyObjects.slice(currentIndex, i).map(obj => ({
|
|
...obj,
|
|
status: STATUS_ADD
|
|
})));
|
|
currentIndex = i;
|
|
}
|
|
list.push({
|
|
...currentKeyObj,
|
|
status: STATUS_KEEP
|
|
});
|
|
currentIndex += 1;
|
|
hit = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If not hit, it means key is removed
|
|
if (!hit) {
|
|
list.push({
|
|
...keyObj,
|
|
status: STATUS_REMOVE
|
|
});
|
|
}
|
|
});
|
|
|
|
// Add rest to the list
|
|
if (currentIndex < currentLen) {
|
|
list = list.concat(currentKeyObjects.slice(currentIndex).map(obj => ({
|
|
...obj,
|
|
status: STATUS_ADD
|
|
})));
|
|
}
|
|
|
|
/**
|
|
* Merge same key when it remove and add again:
|
|
* [1 - add, 2 - keep, 1 - remove] -> [1 - keep, 2 - keep]
|
|
*/
|
|
const keys = {};
|
|
list.forEach(({
|
|
key
|
|
}) => {
|
|
keys[key] = (keys[key] || 0) + 1;
|
|
});
|
|
const duplicatedKeys = Object.keys(keys).filter(key => keys[key] > 1);
|
|
duplicatedKeys.forEach(matchKey => {
|
|
// Remove `STATUS_REMOVE` node.
|
|
list = list.filter(({
|
|
key,
|
|
status
|
|
}) => key !== matchKey || status !== STATUS_REMOVE);
|
|
|
|
// Update `STATUS_ADD` to `STATUS_KEEP`
|
|
list.forEach(node => {
|
|
if (node.key === matchKey) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
node.status = STATUS_KEEP;
|
|
}
|
|
});
|
|
});
|
|
return list;
|
|
} |