Research
Security News
Malicious npm Package Typosquats react-login-page to Deploy Keylogger
Socket researchers unpack a typosquatting package with malicious code that logs keystrokes and exfiltrates sensitive data to a remote server.
diff-sequences
Advanced tools
Package description
The diff-sequences package is a utility for finding the longest common subsequence of items in two sequences (arrays). It is typically used for comparing sequences to determine the changes necessary to convert one sequence into another, which is useful in various applications such as diff tools, merging changes, and more.
Finding Longest Common Subsequence (LCS)
This feature allows you to find the longest common subsequence between two arrays. The provided code sample demonstrates how to use the package to compare two arrays and log the LCS.
const diff = require('diff-sequences');
const a = [1, 2, 3, 4, 5];
const b = [2, 3, 5];
const isCommon = (aIndex, bIndex) => a[aIndex] === b[bIndex];
let result = '';
const foundSubsequence = diff(a.length, b.length, isCommon, (nCommon, aCommon, bCommon) => {
for (let i = 0; i < nCommon; i++) {
result += a[aCommon + i];
}
});
console.log(result); // '235'
fast-diff is a JavaScript implementation of the O(ND) difference algorithm. It is used to quickly compute the difference between two strings and can return the result as an array of parts indicating if they are in the first string, second string, or both. It is similar to diff-sequences but is specifically optimized for string comparison rather than generic sequences.
jsdiff (also known as diff) is a library that provides several algorithms for string comparison, including character-by-character, word-by-word, and line-by-line diffs. It offers more features than diff-sequences, such as applying patches or computing diffs in different formats (e.g., JSON). It is more feature-rich but also larger and potentially slower for simple LCS computations.
google-diff-match-patch is a comprehensive library for performing operations required for synchronizing plain text. It includes algorithms for diff, match, and patch operations. It is more complex and feature-complete compared to diff-sequences, which is more focused on the specific task of finding the longest common subsequence.
Changelog
24.3.0
We skipped 24.2.0 because a draft was accidentally published. Please use 24.3.0
or a newer version instead.
[expect]
: Improve report when matcher fails, part 10 (#7960)[expect]
: Improve report when matcher fails, part 11 (#8008)[expect]
: Improve report when matcher fails, part 12 (#8033)[expect]
: Improve report when matcher fails, part 7 (#7866)[expect]
: Improve report when matcher fails, part 8 (#7876)[expect]
: Improve report when matcher fails, part 9 (#7940)[jest-circus/jest-jasmine2]
Warn if describe returns a value (#7852)[jest-config]
Print error information on preset normalization error (#7935)[jest-get-type]
Add isPrimitive
function (#7708)[jest-haste-map]
Add skipPackageJson
option (#7778)[jest-util]
Add isPromise
(#7852)[pretty-format]
Support React.memo
(#7891)[expect]
Fix toStrictEqual
not considering arrays with objects having undefined values correctly (#7938)[expect]
Fix custom async matcher stack trace (#7652)[expect]
Fix non-object received value in toHaveProperty (#7986, #8067)[expect]
Fix non-symmetric equal for Number (#7948)[expect]
Remove duck typing and obsolete browser support code when comparing DOM nodes and use DOM-Level-3 API instead (#7995)[jest-changed-files]
Fix getChangedFilesFromRoots
to not return parts of the commit messages as if they were files, when the commit messages contained multiple paragraphs (#7961)[jest-changed-files]
Fix pattern for HG changed files (#8066)[jest-changed-files]
Improve default file selection for Mercurial repos (#7880)[jest-circus]
Fix bug with test.only (#7888)[jest-circus]
: Throw explicit error when errors happen after test is considered complete (#8005)[jest-cli]
Fix prototype pollution vulnerability in dependency (#7904)[jest-cli]
Refactor -o
and --coverage
combined (#7611)[jest-environment-node]
Add missing globals: TextEncoder and TextDecoder (#8022)[jest-haste-map]
Enforce uniqueness in names (mocks and haste ids) (#8002)[jest-jasmine2]
: Throw explicit error when errors happen after test is considered complete (#8005)[jest-mock]
Adds a type check to prototype
to allow mocks of objects with a primitive prototype
property. (#8040)[jest-transform]
Normalize config and remove unnecessary checks, convert TestUtils.js
to TypeScript (#7801)[jest-util]
Make sure to not fail if unable to assign toStringTag
to the process
object, which is read only in Node 12 (#8050)[jest-validate]
Fix validating async functions (#7894)[jest-worker]
Fix jest-worker
when using pre-allocated jobs (#7934)[static]
Remove console log '-' on the front page (#7977)[*]
: Setup building, linting and testing of TypeScript (#7808, #7855, #7951)[@jest/console]
: Extract custom console
implementations from jest-util
into a new separate package (#8030)[@jest/core]
Create new package, which is jest-cli
minus yargs
and prompts
(#7696)[@jest/core]
: Migrate to TypeScript (#7998)[@jest/fake-timers]
: Extract FakeTimers class from jest-util
into a new separate package (#7987)[@jest/reporter]
: New package extracted from jest-cli
(#7902)[@jest/reporters]
: Migrate to TypeScript (#7994, #8045)[@jest/source-map]
: Extract getCallsite
function from jest-util
into a new separate package (#8029)[@jest/test-result]
: Extract TestResult types and helpers into a new separate package (#8034)[@jest/transform]
: Migrate to TypeScript (#7918, #7945)[@jest/transform]
: New package extracted from jest-runtime
(#7915)[@jest/types]
: New package to handle shared types (#7834)[babel-jest]
: Migrate to TypeScript (#7862)[babel-plugin-jest-hoist]
: Migrate to TypeScript (#7898)[diff-sequences]
: Migrate to Typescript (#7820)[docs]
Add missing import to docs (#7928)[docs]
Update automock configuration, add note related to manual mocks (#8051)[docs]
Update/Organize TestSequencer and testSchedulerHelper code comments(#7984)[docs]
: Fix image paths in SnapshotTesting.md for current and version 24 (#7872)[docs]
: Improve runAllTimers doc (it exhausts the micro-task queue) (#8031)[docs]
: Update CONTRIBUTING.md to add information about running jest with jest-circus
locally (#8013).[expect]
: Migrate to TypeScript (#7919, #8028)[jest-changed-files]
: Migrate to TypeScript (#7827)[jest-circus]
: Migrate to TypeScript (#7916)[jest-cli]
: Migrate to TypeScript (#8024)[jest-diff]
: Migrate to TypeScript (#7824, #8027)[jest-docblock]
: Migrate to TypeScript (#7836)[jest-each]
: Migrate to Typescript (#8007)[jest-each]
: Refactor into multiple files with better types (#8018)[jest-environment-jsdom]
: Migrate to TypeScript (#8003)[jest-environment-node]
: Migrate to TypeScript (#7985)[jest-get-type]
: Migrate to TypeScript (#7818)[jest-haste-map]
: Migrate to TypeScript (#7854, #7951)[jest-jasmine2]
: TS migration (#7970)[jest-leak-detector]
: Migrate to TypeScript (#7825)[jest-matcher-utils]
: Migrate to TypeScript (#7835)[jest-message-util]
: Migrate to TypeScript (#7834)[jest-mock]
: Migrate to TypeScript (#7847, #7850, #7971)[jest-phabricator]
: Migrate to TypeScript (#7965)[jest-regex-util]
: Migrate to TypeScript (#7822)[jest-repl]
: Migrate to TypeScript (#8000)[jest-resolve-dependencies]
: Migrate to TypeScript (#7922)[jest-resolve]
: Migrate to TypeScript (#7871)[jest-runner]
: Migrate to TypeScript (#7968)[jest-runtime]
: Migrate to TypeScript (#7964, #7988)[jest-serializer]
: Migrate to TypeScript (#7841)[jest-snapshot]
: Migrate to TypeScript (#7899)[jest-util]
: Migrate to TypeScript (#7844, #8021)[jest-validate]
: Migrate to TypeScript (#7991)[jest-watcher]
: Migrate to TypeScript (#7843)[jest-worker]
: Migrate to TypeScript (#7853)[jest]
: Migrate to TypeScript (#8024)[pretty-format]
: Migrate to TypeScript (#7809, #7972)[jest-haste-map]
Optimize haste map tracking of deleted files with Watchman. (#8056)Readme
Compare items in two sequences to find a longest common subsequence.
The items not in common are the items to delete or insert in a shortest edit script.
To maximize flexibility and minimize memory, you write callback functions as configuration:
Input function isCommon(aIndex, bIndex)
compares items at indexes in the sequences and returns a truthy/falsey value. This package might call your function more than once for some pairs of indexes.
===
operator, Object.is
method, or other criterion.Output function foundSubsequence(nCommon, aCommon, bCommon)
receives the number of adjacent items and starting indexes of each common subsequence. If sequences do not have common items, then this package does not call your function.
If N is the sum of lengths of sequences and L is length of a longest common subsequence, then D = N – 2L is the number of differences in the corresponding shortest edit script.
An O(ND) Difference Algorithm and Its Variations by Eugene W. Myers is fast when sequences have few differences.
This package implements the linear space variation with optimizations so it is fast even when sequences have many differences.
To add this package as a dependency of a project, do either of the following:
npm install diff-sequences
yarn add diff-sequences
To use diff
as the name of the default export from this package, do either of the following:
var diff = require('diff-sequences'); // CommonJS modules
import diff from 'diff-sequences'; // ECMAScript modules
Call diff
with the lengths of sequences and your callback functions:
/* eslint-disable no-var */
var a = ['a', 'b', 'c', 'a', 'b', 'b', 'a'];
var b = ['c', 'b', 'a', 'b', 'a', 'c'];
function isCommon(aIndex, bIndex) {
return a[aIndex] === b[bIndex];
}
function foundSubsequence(nCommon, aCommon, bCommon) {
// see examples
}
diff(a.length, b.length, isCommon, foundSubsequence);
Some sequences (for example, a
and b
in the example of usage) have more than one longest common subsequence.
This package finds the following common items:
comparisons of common items | values | output arguments |
---|---|---|
a[2] === b[0] | 'c' | foundSubsequence(1, 2, 0) |
a[4] === b[1] | 'b' | foundSubsequence(1, 4, 1) |
a[5] === b[3] && a[6] === b[4] | 'b', 'a' | foundSubsequence(2, 5, 3) |
The “edit graph” analogy in the Myers paper shows the following common items:
comparisons of common items | values |
---|---|
a[2] === b[0] | 'c' |
a[3] === b[2] && a[4] === b[3] | 'a', 'b' |
a[6] === b[4] | 'a' |
Various packages which implement the Myers algorithm will always agree on the length of a longest common subsequence, but might sometimes disagree on which items are in it.
/* eslint-disable no-var */
// Return length of longest common subsequence according to === operator.
function countCommonItems(a, b) {
var n = 0;
function isCommon(aIndex, bIndex) {
return a[aIndex] === b[bIndex];
}
function foundSubsequence(nCommon) {
n += nCommon;
}
diff(a.length, b.length, isCommon, foundSubsequence);
return n;
}
var commonLength = countCommonItems(
['a', 'b', 'c', 'a', 'b', 'b', 'a'],
['c', 'b', 'a', 'b', 'a', 'c'],
);
category of items | expression | value |
---|---|---|
in common | commonLength | 4 |
to delete from a | a.length - commonLength | 3 |
to insert from b | b.length - commonLength | 2 |
If the length difference b.length - a.length
is:
a
b
a
and insert from b
a
and insert from b
In this example, 6 - 7
is:
1
is the minimum number of items to delete from a
2
is the number of additional items to delete from a
and insert from b
// Return array of items in longest common subsequence according to Object.is method.
const findCommonItems = (a, b) => {
const array = [];
diff(
a.length,
b.length,
(aIndex, bIndex) => Object.is(a[aIndex], b[bIndex]),
(nCommon, aCommon) => {
for (; nCommon !== 0; nCommon -= 1, aCommon += 1) {
array.push(a[aCommon]);
}
},
);
return array;
};
const commonItems = findCommonItems(
['a', 'b', 'c', 'a', 'b', 'b', 'a'],
['c', 'b', 'a', 'b', 'a', 'c'],
);
i | commonItems[i] | aIndex |
---|---|---|
0 | 'c' | 2 |
1 | 'b' | 4 |
2 | 'b' | 5 |
3 | 'a' | 6 |
Instead of slicing array-like objects, you can adjust indexes in your callback functions.
// Diff index intervals that are half open [start, end) like array slice method.
const diffIndexIntervals = (a, aStart, aEnd, b, bStart, bEnd) => {
// Validate: 0 <= aStart and aStart <= aEnd and aEnd <= a.length
// Validate: 0 <= bStart and bStart <= bEnd and bEnd <= b.length
diff(
aEnd - aStart,
bEnd - bStart,
(aIndex, bIndex) => Object.is(a[aStart + aIndex], b[bStart + bIndex]),
(nCommon, aCommon, bCommon) => {
// aStart + aCommon, bStart + bCommon
},
);
// After the last common subsequence, do any remaining work.
};
Linux or Unix has a diff
command to compare files line by line. Its output is a shortest edit script:
// Given zero-based half-open range [start, end) of array indexes,
// return one-based closed range [start + 1, end] as string.
const getRange = (start, end) =>
start + 1 === end ? `${start + 1}` : `${start + 1},${end}`;
// Given index intervals of lines to delete or insert, or both, or neither,
// push formatted diff lines onto array.
const pushDelIns = (aLines, aIndex, aEnd, bLines, bIndex, bEnd, array) => {
const deleteLines = aIndex !== aEnd;
const insertLines = bIndex !== bEnd;
const changeLines = deleteLines && insertLines;
if (changeLines) {
array.push(getRange(aIndex, aEnd) + 'c' + getRange(bIndex, bEnd));
} else if (deleteLines) {
array.push(getRange(aIndex, aEnd) + 'd' + String(bIndex));
} else if (insertLines) {
array.push(String(aIndex) + 'a' + getRange(bIndex, bEnd));
} else {
return;
}
for (; aIndex !== aEnd; aIndex += 1) {
array.push('< ' + aLines[aIndex]); // delete is less than
}
if (changeLines) {
array.push('---');
}
for (; bIndex !== bEnd; bIndex += 1) {
array.push('> ' + bLines[bIndex]); // insert is greater than
}
};
// Given content of two files, return emulated output of diff utility.
const findShortestEditScript = (a, b) => {
const aLines = a.split('\n');
const bLines = b.split('\n');
const aLength = aLines.length;
const bLength = bLines.length;
const isCommon = (aIndex, bIndex) => aLines[aIndex] === bLines[bIndex];
let aIndex = 0;
let bIndex = 0;
const array = [];
const foundSubsequence = (nCommon, aCommon, bCommon) => {
pushDelIns(aLines, aIndex, aCommon, bLines, bIndex, bCommon, array);
aIndex = aCommon + nCommon; // number of lines compared in a
bIndex = bCommon + nCommon; // number of lines compared in b
};
diff(aLength, bLength, isCommon, foundSubsequence);
// After the last common subsequence, push remaining change lines.
pushDelIns(aLines, aIndex, aLength, bLines, bIndex, bLength, array);
return array.length === 0 ? '' : array.join('\n') + '\n';
};
Here is simplified code to format changed and unchanged lines in expected and received values after a test fails in Jest:
// Format diff with minus or plus for change lines and space for common lines.
const formatDiffLines = (a, b) => {
// Jest depends on pretty-format package to serialize objects as strings.
// Unindented for comparison to avoid distracting differences:
const aLinesUn = format(a, {indent: 0 /*, other options*/}).split('\n');
const bLinesUn = format(b, {indent: 0 /*, other options*/}).split('\n');
// Indented to display changed and unchanged lines:
const aLinesIn = format(a, {indent: 2 /*, other options*/}).split('\n');
const bLinesIn = format(b, {indent: 2 /*, other options*/}).split('\n');
const aLength = aLinesIn.length; // Validate: aLinesUn.length === aLength
const bLength = bLinesIn.length; // Validate: bLinesUn.length === bLength
const isCommon = (aIndex, bIndex) => aLinesUn[aIndex] === bLinesUn[bIndex];
// Only because the GitHub Flavored Markdown doc collapses adjacent spaces,
// this example code and the following table represent spaces as middle dots.
let aIndex = 0;
let bIndex = 0;
const array = [];
const foundSubsequence = (nCommon, aCommon, bCommon) => {
for (; aIndex !== aCommon; aIndex += 1) {
array.push('-·' + aLinesIn[aIndex]); // delete is minus
}
for (; bIndex !== bCommon; bIndex += 1) {
array.push('+·' + bLinesIn[bIndex]); // insert is plus
}
for (; nCommon !== 0; nCommon -= 1, aIndex += 1, bIndex += 1) {
// For common lines, received indentation seems more intuitive.
array.push('··' + bLinesIn[bIndex]); // common is space
}
};
diff(aLength, bLength, isCommon, foundSubsequence);
// After the last common subsequence, push remaining change lines.
for (; aIndex !== aLength; aIndex += 1) {
array.push('-·' + aLinesIn[aIndex]);
}
for (; bIndex !== bLength; bIndex += 1) {
array.push('+·' + bLinesIn[bIndex]);
}
return array;
};
const expected = {
searching: '',
sorting: {
ascending: true,
fieldKey: 'what',
},
};
const received = {
searching: '',
sorting: [
{
descending: false,
fieldKey: 'what',
},
],
};
const diffLines = formatDiffLines(expected, received);
If N is the sum of lengths of sequences and L is length of a longest common subsequence, then N – L is length of an array of diff lines. In this example, N is 7 + 9, L is 5, and N – L is 11.
i | diffLines[i] | aIndex | bIndex |
---|---|---|---|
0 | '··Object {' | 0 | 0 |
1 | '····"searching": "",' | 1 | 1 |
2 | '-···"sorting": Object {' | 2 | |
3 | '-·····"ascending": true,' | 3 | |
4 | '+·····"sorting": Array [' | 2 | |
5 | '+·······Object {' | 3 | |
6 | '+·········"descending": false,' | 4 | |
7 | '··········"fieldKey": "what",' | 4 | 5 |
8 | '········},' | 5 | 6 |
9 | '+·····],' | 7 | |
10 | '··}' | 6 | 8 |
Here is simplified code to find changed and unchanged substrings within adjacent changed lines in expected and received values after a test fails in Jest:
// Return diff items for strings (compatible with diff-match-patch package).
const findDiffItems = (a, b) => {
const isCommon = (aIndex, bIndex) => a[aIndex] === b[bIndex];
let aIndex = 0;
let bIndex = 0;
const array = [];
const foundSubsequence = (nCommon, aCommon, bCommon) => {
if (aIndex !== aCommon) {
array.push([-1, a.slice(aIndex, aCommon)]); // delete is -1
}
if (bIndex !== bCommon) {
array.push([1, b.slice(bIndex, bCommon)]); // insert is 1
}
aIndex = aCommon + nCommon; // number of characters compared in a
bIndex = bCommon + nCommon; // number of characters compared in b
array.push([0, a.slice(aCommon, aIndex)]); // common is 0
};
diff(a.length, b.length, isCommon, foundSubsequence);
// After the last common subsequence, push remaining change items.
if (aIndex !== a.length) {
array.push([-1, a.slice(aIndex)]);
}
if (bIndex !== b.length) {
array.push([1, b.slice(bIndex)]);
}
return array;
};
const expectedDeleted = ['"sorting": Object {', '"ascending": true,'].join(
'\n',
);
const receivedInserted = [
'"sorting": Array [',
'Object {',
'"descending": false,',
].join('\n');
const diffItems = findDiffItems(expectedDeleted, receivedInserted);
i | diffItems[i][0] | diffItems[i][1] |
---|---|---|
0 | 0 | '"sorting": ' |
1 | 1 | 'Array [\n' |
2 | 0 | 'Object {\n"' |
3 | -1 | 'a' |
4 | 1 | 'de' |
5 | 0 | 'scending": ' |
6 | -1 | 'tru' |
7 | 1 | 'fals' |
8 | 0 | 'e,' |
The length difference b.length - a.length
is equal to the sum of diffItems[i][0]
values times diffItems[i][1]
lengths. In this example, the difference 48 - 38
is equal to the sum 10
.
category of diff item | [0] | [1] lengths | subtotal |
---|---|---|---|
in common | 0 | 11 + 10 + 11 + 2 | 0 |
to delete from a | –1 | 1 + 3 | -4 |
to insert from b | 1 | 8 + 2 + 4 | 14 |
Instead of formatting the changed substrings with escape codes for colors in the foundSubsequence
function to save memory, this example spends memory to gain flexibility before formatting, so a separate heuristic algorithm might modify the generic array of diff items to show changes more clearly:
i | diffItems[i][0] | diffItems[i][1] |
---|---|---|
6 | -1 | 'true' |
7 | 1 | 'false' |
8 | 0 | ',' |
For expected and received strings of serialized data, the result of finding changed lines, and then finding changed substrings within adjacent changed lines (as in the preceding two examples) sometimes displays the changes in a more intuitive way than the result of finding changed substrings, and then splitting them into changed and unchanged lines.
FAQs
Unknown package
The npm package diff-sequences receives a total of 22,849,201 weekly downloads. As such, diff-sequences popularity was classified as popular.
We found that diff-sequences demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers unpack a typosquatting package with malicious code that logs keystrokes and exfiltrates sensitive data to a remote server.
Security News
The JavaScript community has launched the e18e initiative to improve ecosystem performance by cleaning up dependency trees, speeding up critical parts of the ecosystem, and documenting lighter alternatives to established tools.
Product
Socket now supports four distinct alert actions instead of the previous two, and alert triaging allows users to override the actions taken for all individual alerts.