From 339b7cdd2f42dd835738ba3c0177cc466a7a6907 Mon Sep 17 00:00:00 2001 From: Eric Ferreira Date: Mon, 20 Mar 2023 21:36:24 -0400 Subject: [PATCH 1/2] feat: expand types to allow differently typed arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a comparator function, there's not a hard requirement for the two arrays to be compatible types. The comparator tells us whether they are "equal", and that's enough. This will maintain the integrity between which type corresponds to what in the comparators, as well as the functions where appropriate. This doesn't touch `getPatch` or `applyPatch`, because patching doesn’t make sense if the types don’t match. --- src/diff/diff.ts | 14 +++++++------- src/diff/lcs.ts | 18 +++++++++--------- src/diff/same.ts | 6 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/diff/diff.ts b/src/diff/diff.ts index 22dd4d8..a911a05 100644 --- a/src/diff/diff.ts +++ b/src/diff/diff.ts @@ -1,16 +1,16 @@ import bestSubSequence from './lcs'; -export interface DiffData { +export interface DiffData { removed: T[]; - added: T[]; + added: U[]; } -export function diff( +export function diff( a: T[], - b: T[], - compareFunc: (ia: T, ib: T) => boolean = (ia: T, ib: T) => ia === ib, -): DiffData { - const ret: DiffData = { + b: U[], + compareFunc: (ia: T, ib: U) => boolean = (ia: T, ib: U) => ia === (ib as unknown as T), +): DiffData { + const ret: DiffData = { removed: [], added: [], }; diff --git a/src/diff/lcs.ts b/src/diff/lcs.ts index aade358..e3cf928 100644 --- a/src/diff/lcs.ts +++ b/src/diff/lcs.ts @@ -1,4 +1,4 @@ -function lcs(a: T[], b: T[], compareFunc: (a: T, b: T) => boolean): number { +function lcs(a: T[], b: U[], compareFunc: (a: T, b: U) => boolean): number { const M = a.length, N = b.length; const MAX = M + N; @@ -39,23 +39,23 @@ enum Direct { all = horizontal | vertical | diagonal, } -function getSolution( +function getSolution( a: T[], aStart: number, aEnd: number, - b: T[], + b: U[], bStart: number, bEnd: number, d: number, startDirect: Direct, endDirect: Direct, - compareFunc: (a: T, b: T) => boolean, + compareFunc: (a: T, b: U) => boolean, elementsChanged: ( type: 'add' | 'remove' | 'same', a: T[], aStart: number, aEnd: number, - b: T[], + b: U[], bStart: number, bEnd: number, ) => void, @@ -285,16 +285,16 @@ function getSolution( ); } -export default function bestSubSequence( +export default function bestSubSequence( a: T[], - b: T[], - compareFunc: (a: T, b: T) => boolean, + b: U[], + compareFunc: (a: T, b: U) => boolean, elementsChanged: ( type: 'add' | 'remove' | 'same', a: T[], aStart: number, aEnd: number, - b: T[], + b: U[], bStart: number, bEnd: number, ) => void, diff --git a/src/diff/same.ts b/src/diff/same.ts index 79f1005..1422765 100644 --- a/src/diff/same.ts +++ b/src/diff/same.ts @@ -1,9 +1,9 @@ import bestSubSequence from './lcs'; -export default function ( +export default function ( a: T[], - b: T[], - compareFunc: (ia: T, ib: T) => boolean = (ia: T, ib: T) => ia === ib, + b: U[], + compareFunc: (ia: T, ib: U) => boolean = (ia: T, ib: U) => ia === (ib as unknown as T), ): T[] { const ret: T[] = []; bestSubSequence(a, b, compareFunc, (type, oldArr, oldStart, oldEnd) => { From 395453b709e052d3c223850948dfdb81bfb84001 Mon Sep 17 00:00:00 2001 From: Eric Ferreira Date: Tue, 21 Mar 2023 09:07:20 -0400 Subject: [PATCH 2/2] Add test cases for changed functions --- src/index.ts | 3 ++- src/test/diff.spec.ts | 12 ++++++++++++ src/test/index.spec.ts | 35 +++++++++++++++++++++++++++++++++++ src/test/same.spec.ts | 12 ++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 454ec68..b72c6f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ +import bestSubSequence from './diff/lcs'; import same from './diff/same'; -export { same }; +export { bestSubSequence, same }; export * from './diff/diff'; diff --git a/src/test/diff.spec.ts b/src/test/diff.spec.ts index f71a098..2735fa0 100644 --- a/src/test/diff.spec.ts +++ b/src/test/diff.spec.ts @@ -63,4 +63,16 @@ describe('Diff', () => { '', ); }); + + it('Functional test on arrays of different types', () => { + assert.deepStrictEqual( + diff.diff([1, 2, 3], ['2', '3', '4'], (l, r) => { + assert.equal(typeof l, 'number'); + assert.equal(typeof r, 'string'); + + return l.toString() === r; + }), + { added: ['4'], removed: [1] }, + ); + }); }); diff --git a/src/test/index.spec.ts b/src/test/index.spec.ts index a8d50f4..9e694c8 100644 --- a/src/test/index.spec.ts +++ b/src/test/index.spec.ts @@ -9,6 +9,41 @@ describe('Index', () => { assert.deepStrictEqual(diff.same([1, 2, 3], [2, 3, 4]), [2, 3]); }); + it('bestSubSequence function in index', () => { + const changes: ( + | { type: 'same' | 'remove'; values: number[] } + | { type: 'add'; values: string[] } + )[] = []; + + diff.bestSubSequence( + [1, 2, 3], + ['2', '3', '4'], + (l, r) => { + assert.equal(typeof l, 'number'); + assert.equal(typeof r, 'string'); + + return l.toString() === r; + }, + (type, a, aStart, aEnd, b, bStart, bEnd) => { + assert.equal(typeof a[0], 'number'); + assert.equal(typeof b[0], 'string'); + + if (type === 'add') { + changes.push({ type, values: b.slice(bStart, bEnd) }); + } else { + changes.push({ type, values: a.slice(aStart, aEnd) }); + } + }, + ); + + assert.deepStrictEqual(changes, [ + { type: 'remove', values: [1] }, + { type: 'same', values: [2] }, + { type: 'same', values: [3] }, + { type: 'add', values: ['4'] }, + ]); + }); + it('diff data and function in index', () => { const result: diff.DiffData = { added: [1, 2], diff --git a/src/test/same.spec.ts b/src/test/same.spec.ts index 2baaa6f..a47bdac 100644 --- a/src/test/same.spec.ts +++ b/src/test/same.spec.ts @@ -144,4 +144,16 @@ describe('Same', () => { ]; assert.deepStrictEqual(same(a, b, compare), result); }); + + it('Functional test on arrays of different types', () => { + assert.deepStrictEqual( + same([1, 2, 3], ['2', '3', '4'], (l, r) => { + assert.equal(typeof l, 'number'); + assert.equal(typeof r, 'string'); + + return l.toString() === r; + }), + [2, 3], + ); + }); });