nhb-toolbox 4.29.0 → 4.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  All notable changes to the package will be documented here.
6
6
 
7
+ ## [4.29.1] - 2026-03-27
8
+
9
+ - **Updated** `getLevenshteinDistance` utility to be more *efficient* by using a *single-dimensional array* instead of a *matrix* for storing intermediate distances.
10
+
7
11
  ## [4.29.0] - 2026-03-26
8
12
 
9
13
  - **Added** new *string utilities* `computeTextDiff` and `getCharacterDifferences` for computing differences between two strings at both *line* and *character* levels.
@@ -3,28 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports._calculateSimilarity = _calculateSimilarity;
4
4
  exports._buildCharLcsTable = _buildCharLcsTable;
5
5
  exports._getLcsIndices = _getLcsIndices;
6
+ const utilities_1 = require("./utilities");
6
7
  function _calculateSimilarity(str1, str2) {
7
- const len1 = str1.length;
8
- const len2 = str2.length;
9
- const maxLen = Math.max(len1, len2);
10
- if (maxLen === 0)
11
- return 1;
12
8
  if (str1 === str2)
13
9
  return 1;
14
- const matrix = Array(len1 + 1)
15
- .fill(null)
16
- .map(() => Array(len2 + 1).fill(0));
17
- for (let i = 0; i <= len1; i++)
18
- matrix[i][0] = i;
19
- for (let j = 0; j <= len2; j++)
20
- matrix[0][j] = j;
21
- for (let i = 1; i <= len1; i++) {
22
- for (let j = 1; j <= len2; j++) {
23
- const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
24
- matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
25
- }
26
- }
27
- const distance = matrix[len1][len2];
10
+ const maxLen = Math.max(str1.length, str2.length);
11
+ if (maxLen === 0)
12
+ return 1;
13
+ const distance = (0, utilities_1.getLevenshteinDistance)(str1, str2);
28
14
  return 1 - distance / maxLen;
29
15
  }
30
16
  function _buildCharLcsTable(original, modified) {
@@ -6,19 +6,27 @@ const extractNumbersFromString = (input) => {
6
6
  return (input.match(/\d+/g) || [])?.map(Number);
7
7
  };
8
8
  exports.extractNumbersFromString = extractNumbersFromString;
9
- const getLevenshteinDistance = (a, b) => {
10
- const lenA = a?.length;
11
- const lenB = b?.length;
12
- const dp = Array.from({ length: lenA + 1 }, (_, i) => Array.from({ length: lenB + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)));
9
+ const getLevenshteinDistance = (str1, str2) => {
10
+ if (str1 === str2)
11
+ return 0;
12
+ const lenA = str1?.length;
13
+ const lenB = str2?.length;
14
+ if (lenA < lenB) {
15
+ return (0, exports.getLevenshteinDistance)(str2, str1);
16
+ }
17
+ let prev = Array.from({ length: lenB + 1 }, (_, j) => j);
18
+ let curr = new Array(lenB + 1);
13
19
  for (let i = 1; i <= lenA; i++) {
20
+ curr[0] = i;
14
21
  for (let j = 1; j <= lenB; j++) {
15
- dp[i][j] =
16
- a[i - 1] === b[j - 1]
17
- ? dp[i - 1][j - 1]
18
- : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
22
+ curr[j] =
23
+ str1[i - 1] === str2[j - 1]
24
+ ? prev[j - 1]
25
+ : 1 + Math.min(prev[j], curr[j - 1], prev[j - 1]);
19
26
  }
27
+ [prev, curr] = [curr, prev];
20
28
  }
21
- return dp[lenA][lenB];
29
+ return prev[lenB];
22
30
  };
23
31
  exports.getLevenshteinDistance = getLevenshteinDistance;
24
32
  function countWords(text) {
@@ -1,3 +1,4 @@
1
+ import type { TupleOf } from '../utils/types';
1
2
  /**
2
3
  * Calculates the similarity between two strings using the Levenshtein edit distance.
3
4
  *
@@ -22,4 +23,4 @@ export declare function _buildCharLcsTable(original: string, modified: string):
22
23
  * @param lcs The precomputed LCS table from {@link buildCharLcsTable}.
23
24
  * @returns A tuple `[origMatched, modMatched]` — sets of matched character indices for the original and modified strings respectively.
24
25
  */
25
- export declare function _getLcsIndices(original: string, modified: string, lcs: number[][]): Set<number>[];
26
+ export declare function _getLcsIndices(original: string, modified: string, lcs: number[][]): TupleOf<Set<number>, 2>;
@@ -5,12 +5,20 @@
5
5
  */
6
6
  export declare const extractNumbersFromString: (input: string) => number[];
7
7
  /**
8
- * * Computes the Levenshtein distance between two strings.
9
- * @param a - First string.
10
- * @param b - Second string.
8
+ * * Computes the Levenshtein distance between two strings (space optimized).
9
+ * @param str1 - First string to compare.
10
+ * @param str2 - Second string to compare.
11
11
  * @returns The Levenshtein distance between the two strings.
12
+ *
13
+ * @remarks
14
+ * - The Levenshtein distance is the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into the other.
15
+ * - This implementation uses only O(min(len(a), len(b))) space by keeping only the current and previous rows of the distance matrix.
16
+ *
17
+ * @example
18
+ * const distance = getLevenshteinDistance('kitten', 'sitting');
19
+ * console.log(distance); // Output: 3
12
20
  */
13
- export declare const getLevenshteinDistance: (a: string, b: string) => number;
21
+ export declare const getLevenshteinDistance: (str1: string, str2: string) => number;
14
22
  /**
15
23
  * * Counts the number of words in a string, supporting multiple languages and scripts.
16
24
  *
@@ -268,6 +268,8 @@ export type ExtractRequired<T> = {
268
268
  * type Role = TupleToUnion<typeof roles>; // "admin" | "user" | "guest"
269
269
  */
270
270
  export type TupleToUnion<T extends readonly unknown[]> = T[number];
271
+ /** Internal helper type to build a tuple of length N. */
272
+ type $TupleOf<T, N extends number, R extends unknown[] = []> = R['length'] extends N ? R : $TupleOf<T, N, [...R, T]>;
271
273
  /**
272
274
  * * Creates a tuple type of a given length with elements of type T
273
275
  *
@@ -281,7 +283,7 @@ export type TupleToUnion<T extends readonly unknown[]> = T[number];
281
283
  * type FiveStrings = TupleOf<string, 5>; // [string, string, string, string, string]
282
284
  * type EmptyTuple = TupleOf<boolean, 0>; // []
283
285
  */
284
- export type TupleOf<T, N extends number, R extends unknown[] = []> = R['length'] extends N ? R : TupleOf<T, N, [...R, T]>;
286
+ export type TupleOf<T, N extends number> = $TupleOf<T, N>;
285
287
  /** * Build a tuple of given length (helper for type-level arithmetic). */
286
288
  export type $BuildTuple<L extends number, T extends unknown[] = []> = T['length'] extends L ? T : $BuildTuple<L, [...T, unknown]>;
287
289
  /** * Produce a union of numbers `From | From+1 | ... | To`. */
@@ -1,25 +1,11 @@
1
+ import { getLevenshteinDistance } from './utilities.js';
1
2
  export function _calculateSimilarity(str1, str2) {
2
- const len1 = str1.length;
3
- const len2 = str2.length;
4
- const maxLen = Math.max(len1, len2);
5
- if (maxLen === 0)
6
- return 1;
7
3
  if (str1 === str2)
8
4
  return 1;
9
- const matrix = Array(len1 + 1)
10
- .fill(null)
11
- .map(() => Array(len2 + 1).fill(0));
12
- for (let i = 0; i <= len1; i++)
13
- matrix[i][0] = i;
14
- for (let j = 0; j <= len2; j++)
15
- matrix[0][j] = j;
16
- for (let i = 1; i <= len1; i++) {
17
- for (let j = 1; j <= len2; j++) {
18
- const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
19
- matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
20
- }
21
- }
22
- const distance = matrix[len1][len2];
5
+ const maxLen = Math.max(str1.length, str2.length);
6
+ if (maxLen === 0)
7
+ return 1;
8
+ const distance = getLevenshteinDistance(str1, str2);
23
9
  return 1 - distance / maxLen;
24
10
  }
25
11
  export function _buildCharLcsTable(original, modified) {
@@ -1,19 +1,27 @@
1
1
  export const extractNumbersFromString = (input) => {
2
2
  return (input.match(/\d+/g) || [])?.map(Number);
3
3
  };
4
- export const getLevenshteinDistance = (a, b) => {
5
- const lenA = a?.length;
6
- const lenB = b?.length;
7
- const dp = Array.from({ length: lenA + 1 }, (_, i) => Array.from({ length: lenB + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)));
4
+ export const getLevenshteinDistance = (str1, str2) => {
5
+ if (str1 === str2)
6
+ return 0;
7
+ const lenA = str1?.length;
8
+ const lenB = str2?.length;
9
+ if (lenA < lenB) {
10
+ return getLevenshteinDistance(str2, str1);
11
+ }
12
+ let prev = Array.from({ length: lenB + 1 }, (_, j) => j);
13
+ let curr = new Array(lenB + 1);
8
14
  for (let i = 1; i <= lenA; i++) {
15
+ curr[0] = i;
9
16
  for (let j = 1; j <= lenB; j++) {
10
- dp[i][j] =
11
- a[i - 1] === b[j - 1]
12
- ? dp[i - 1][j - 1]
13
- : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
17
+ curr[j] =
18
+ str1[i - 1] === str2[j - 1]
19
+ ? prev[j - 1]
20
+ : 1 + Math.min(prev[j], curr[j - 1], prev[j - 1]);
14
21
  }
22
+ [prev, curr] = [curr, prev];
15
23
  }
16
- return dp[lenA][lenB];
24
+ return prev[lenB];
17
25
  };
18
26
  export function countWords(text) {
19
27
  return (text?.match(/\p{L}[\p{L}\p{M}\p{Pd}'’]*|\p{N}+/gu) || [])?.length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nhb-toolbox",
3
- "version": "4.29.0",
3
+ "version": "4.29.1",
4
4
  "description": "A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",