@technobuddha/library
Version:
A large library of useful functions
88 lines (79 loc) • 2.48 kB
text/typescript
import { create2dArray } from './create2d-array.ts';
/**
* Options for configuring the {@link longestCommonSubsequence} calculation.
* @typeParam T - Type of objects in the arrays.
* @group Array
* @category Analysis
*/
export type LongestCommonSubsequenceOptions<T> = {
/**
* Function that acts as a custom comparator
* for the array objects. Function should return true if objects are equal, otherwise false.
*/
compare?: (this: void, a: T, b: T) => boolean;
/**
* Function used to decide what to return
* as a result subsequence. It accepts 2 arguments: index of common element
* in the first array and index in the second. The default function returns
* element from the first array.
*/
collect?: (this: void, i1: number, i2: number) => T;
};
/**
* Determine the longest possible array that is subsequence of both of given arrays.
* @remarks
* Implementation of [Longest Common Subsequence](https://en.wikipedia.org/wiki/Longest_common_subsequence) algorithm.
* @typeParam T - Type of objects in the arrays.
* @param array1 - First array of objects.
* @param array2 - Second array of objects.
* @param options - Functions to compare and collect elements from the two arrays
* @returns A list of objects that are common to both arrays
* such that there is no common subsequence with size greater than the
* length of the list.
* @example
* ```typescript
* longestCommonSubsequence(
* ['a', 'b', 'c', ' ', 'd', 'e', 'f'],
* ['a', 'c', ' ', 'd', 'e', 'c'],
* ); // ['a', 'c', ' ', 'd', 'e']
* ```
* @group Array
* @category Analysis
*/
export function longestCommonSubsequence<T>(
array1: ArrayLike<T>,
array2: ArrayLike<T>,
{
compare = (a, b) => a === b,
collect = (i1, _i2) => array1[i1],
}: LongestCommonSubsequenceOptions<T> = {},
): T[] {
const l1 = array1.length;
const l2 = array2.length;
const c = create2dArray(l1 + 1, l2 + 1, 0);
let i: number;
let j: number;
for (i = 1; i <= l1; i++) {
for (j = 1; j <= l2; j++) {
c[i][j] =
compare(array1[i - 1], array2[j - 1]) ?
c[i - 1][j - 1] + 1
: Math.max(c[i - 1][j], c[i][j - 1]);
}
}
const result = [] as T[];
i = l1;
j = l2;
while (i > 0 && j > 0) {
if (compare(array1[i - 1], array2[j - 1])) {
result.unshift(collect(i - 1, j - 1));
i--;
j--;
} else if (c[i - 1][j] > c[i][j - 1]) {
i--;
} else {
j--;
}
}
return result;
}