diff-sequences is a high-performance TypeScript library that implements the linear space variation of Eugene W. Myers' O(ND) difference algorithm. It finds the longest common subsequence between two sequences using a callback-based API that maximizes flexibility and minimizes memory usage, making it ideal for building diff tools, version control systems, and test assertion libraries.
npm install diff-sequencesimport diffSequence from "diff-sequences";For CommonJS:
const diffSequence = require("diff-sequences").default;import diffSequence from "diff-sequences";
const a = ['a', 'b', 'c', 'a', 'b', 'b', 'a'];
const b = ['c', 'b', 'a', 'b', 'a', 'c'];
// Define comparison function
function isCommon(aIndex: number, bIndex: number): boolean {
return a[aIndex] === b[bIndex];
}
// Define subsequence handler
function foundSubsequence(nCommon: number, aCommon: number, bCommon: number): void {
console.log(`Found ${nCommon} common items starting at a[${aCommon}] and b[${bCommon}]`);
}
// Execute diff algorithm
diffSequence(a.length, b.length, isCommon, foundSubsequence);diff-sequences is built around Myers' O(ND) difference algorithm with several key optimizations:
Finds the longest common subsequence between two sequences using Myers' algorithm.
/**
* Compare items in two sequences to find a longest common subsequence.
* This is the default export of the diff-sequences package.
* @param aLength - Length of the first sequence
* @param bLength - Length of the second sequence
* @param isCommon - Function to compare items at given indexes
* @param foundSubsequence - Callback for each common subsequence found
*/
export default function diffSequence(
aLength: number,
bLength: number,
isCommon: IsCommon,
foundSubsequence: FoundSubsequence
): void;Usage Examples:
// Count common items
function countCommonItems(a: any[], b: any[]): number {
let count = 0;
diffSequence(
a.length,
b.length,
(aIndex, bIndex) => a[aIndex] === b[bIndex],
(nCommon) => { count += nCommon; }
);
return count;
}
// Find common items
function findCommonItems<T>(a: T[], b: T[]): T[] {
const common: T[] = [];
diffSequence(
a.length,
b.length,
(aIndex, bIndex) => Object.is(a[aIndex], b[bIndex]),
(nCommon, aCommon) => {
for (let i = 0; i < nCommon; i++) {
common.push(a[aCommon + i]);
}
}
);
return common;
}
// Generate diff output
function generateDiff(a: string[], b: string[]): string[] {
let aIndex = 0;
let bIndex = 0;
const result: string[] = [];
diffSequence(
a.length,
b.length,
(aI, bI) => a[aI] === b[bI],
(nCommon, aCommon, bCommon) => {
// Add deletions
for (; aIndex < aCommon; aIndex++) {
result.push(`- ${a[aIndex]}`);
}
// Add insertions
for (; bIndex < bCommon; bIndex++) {
result.push(`+ ${b[bIndex]}`);
}
// Add common lines
for (let i = 0; i < nCommon; i++, aIndex++, bIndex++) {
result.push(` ${a[aIndex]}`);
}
}
);
// Handle remaining items
for (; aIndex < a.length; aIndex++) {
result.push(`- ${a[aIndex]}`);
}
for (; bIndex < b.length; bIndex++) {
result.push(`+ ${b[bIndex]}`);
}
return result;
}/**
* Input callback function that compares items at indexes in the sequences
* @param aIndex - Index in first sequence (0 <= aIndex < aLength)
* @param bIndex - Index in second sequence (0 <= bIndex < bLength)
* @returns true if items are considered equal, false otherwise
*/
type IsCommon = (aIndex: number, bIndex: number) => boolean;
/**
* Output callback function that receives information about each common subsequence
* @param nCommon - Number of adjacent common items (always > 0)
* @param aCommon - Starting index in first sequence (0 <= aCommon < aLength)
* @param bCommon - Starting index in second sequence (0 <= bCommon < bLength)
*/
type FoundSubsequence = (
nCommon: number,
aCommon: number,
bCommon: number
) => void;
/**
* Container for callback functions used internally
*/
interface Callbacks {
foundSubsequence: FoundSubsequence;
isCommon: IsCommon;
}The function performs validation on all parameters and throws errors for invalid inputs:
// Example error handling
try {
diffSequence(
10,
20,
(a, b) => a === b, // Valid function
(nCommon, aCommon, bCommon) => {
console.log(`Found ${nCommon} common items`);
}
);
} catch (error) {
if (error instanceof TypeError) {
console.error('Invalid parameter type:', error.message);
} else if (error instanceof RangeError) {
console.error('Parameter out of range:', error.message);
}
}