diff --git a/README.md b/README.md index 4217121..abab8ee 100644 --- a/README.md +++ b/README.md @@ -178,8 +178,8 @@ Compares two arrays and return a diff for each value: - index change: `prevIndex`, `newIndex`, `indexDiff` - status: `added`, `deleted`, `equal`, `moved`, `updated` - value -- supports array of primitive values and objects -- ⚠️ doesn't support duplicated values comparison yet (but will soon) +- supports arrays of primitive values and objects +- supports arrays with duplicated values format: diff --git a/src/list-diff.ts b/src/list-diff.ts index f8e33d9..9ca25c5 100644 --- a/src/list-diff.ts +++ b/src/list-diff.ts @@ -42,10 +42,15 @@ export const getListDiff = ( return formatSingleListDiff(prevList as ListData, STATUS.DELETED); } const diff: ListDiff["diff"] = []; + const prevIndexMatches: number[] = []; nextList.forEach((nextValue, i) => { - const prevIndex = prevList.findIndex((prevValue) => - isEqual(prevValue, nextValue) + const prevIndex = prevList.findIndex( + (prevValue, prevIdx) => + isEqual(prevValue, nextValue) && !prevIndexMatches.includes(prevIdx) ); + if (prevIndex > -1) { + prevIndexMatches.push(prevIndex); + } const indexDiff = prevIndex === -1 ? null : i - prevIndex; if (indexDiff === 0) { return diff.push({ @@ -75,7 +80,7 @@ export const getListDiff = ( }); prevList.forEach((prevValue, i) => { - if (!nextList.some((nextValue) => isEqual(nextValue, prevValue))) { + if (!prevIndexMatches.includes(i)) { return diff.splice(i, 0, { value: prevValue, prevIndex: i, diff --git a/test/list-diff.test.ts b/test/list-diff.test.ts index 16fb98b..6ce2b7d 100644 --- a/test/list-diff.test.ts +++ b/test/list-diff.test.ts @@ -84,7 +84,7 @@ describe("getListDiff", () => { ], }); }); - it("detects changed values in a string list", () => { + it("detects changes between string lists", () => { expect( getListDiff( ["mbappe", "mendes", "verratti", "ruiz"], @@ -132,7 +132,7 @@ describe("getListDiff", () => { ], }); }); - it("detects changed values in a number list", () => { + it("detects changes between number lists", () => { expect(getListDiff([54, 234, 76, 0], [54, 200, 0])).toStrictEqual({ type: "list", status: "updated", @@ -175,7 +175,7 @@ describe("getListDiff", () => { ], }); }); - it("detects changed values in an object list", () => { + it("detects changes between object lists", () => { expect( getListDiff( [ @@ -224,4 +224,173 @@ describe("getListDiff", () => { ], }); }); + it("detects changes between lists containing duplicated values", () => { + expect( + getListDiff(["mbappe", "messi"], ["mbappe", "mbappe", "messi"]) + ).toStrictEqual({ + type: "list", + status: "updated", + diff: [ + { + value: "mbappe", + prevIndex: 0, + newIndex: 0, + indexDiff: 0, + status: "equal", + }, + { + value: "mbappe", + prevIndex: null, + newIndex: 1, + indexDiff: null, + status: "added", + }, + { + value: "messi", + prevIndex: 1, + newIndex: 2, + indexDiff: 1, + status: "moved", + }, + ], + }); + expect( + getListDiff( + ["mbappe", "messi", "messi", "mbappe"], + ["mbappe", "messi", "messi"] + ) + ).toStrictEqual({ + type: "list", + status: "updated", + diff: [ + { + value: "mbappe", + prevIndex: 0, + newIndex: 0, + indexDiff: 0, + status: "equal", + }, + { + value: "messi", + prevIndex: 1, + newIndex: 1, + indexDiff: 0, + status: "equal", + }, + { + value: "messi", + prevIndex: 2, + newIndex: 2, + indexDiff: 0, + status: "equal", + }, + { + value: "mbappe", + prevIndex: 3, + newIndex: null, + indexDiff: null, + status: "deleted", + }, + ], + }); + expect( + getListDiff( + [ + false, + true, + true, + undefined, + "hello", + { name: "joe", age: 88 }, + false, + 13, + ], + [ + false, + false, + true, + undefined, + "hello", + { name: "joe", age: 88 }, + false, + { name: "joe", age: 88 }, + ] + ) + ).toStrictEqual({ + type: "list", + status: "updated", + diff: [ + { + value: false, + prevIndex: 0, + newIndex: 0, + indexDiff: 0, + status: "equal", + }, + { + value: false, + prevIndex: 6, + newIndex: 1, + indexDiff: -5, + status: "moved", + }, + { + value: true, + prevIndex: 2, + newIndex: null, + indexDiff: null, + status: "deleted", + }, + { + value: true, + prevIndex: 1, + newIndex: 2, + indexDiff: 1, + status: "moved", + }, + { + value: undefined, + prevIndex: 3, + newIndex: 3, + indexDiff: 0, + status: "equal", + }, + { + value: "hello", + prevIndex: 4, + newIndex: 4, + indexDiff: 0, + status: "equal", + }, + { + value: { name: "joe", age: 88 }, + prevIndex: 5, + newIndex: 5, + indexDiff: 0, + status: "equal", + }, + { + value: 13, + prevIndex: 7, + newIndex: null, + indexDiff: null, + status: "deleted", + }, + { + value: false, + prevIndex: null, + newIndex: 6, + indexDiff: null, + status: "added", + }, + { + value: { name: "joe", age: 88 }, + prevIndex: null, + newIndex: 7, + indexDiff: null, + status: "added", + }, + ], + }); + }); });