src/jsutils/suggestionList.js
function lexicalDistance(aStr, bStr) {
  if (aStr === bStr) {
    return 0;
  }

  let i;
  let j;
  const d = [];
  const a = aStr.toLowerCase();
  const b = bStr.toLowerCase();
  const aLength = a.length;
  const bLength = b.length;

  // Any case change counts as a single edit
  if (a === b) {
    return 1;
  }

  for (i = 0; i <= aLength; i++) {
    d[i] = [i];
  }

  for (j = 1; j <= bLength; j++) {
    d[0][j] = j;
  }

  for (i = 1; i <= aLength; i++) {
    for (j = 1; j <= bLength; j++) {
      const cost = a[i - 1] === b[j - 1] ? 0 : 1;

      d[i][j] = Math.min(
        d[i - 1][j] + 1,
        d[i][j - 1] + 1,
        d[i - 1][j - 1] + cost,
      );

      if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
        d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
      }
    }
  }

  return d[aLength][bLength];
}
function lexicalDistance(aStr, bStr) {
  if (aStr === bStr) {
    return 0;
  }

  let i;
  let j;
  const d = [];
  const a = aStr.toLowerCase();
  const b = bStr.toLowerCase();
  const aLength = a.length;
  const bLength = b.length;

  // Any case change counts as a single edit
  if (a === b) {
    return 1;
  }

  for (i = 0; i <= aLength; i++) {
    d[i] = [i];
  }

  for (j = 1; j <= bLength; j++) {
    d[0][j] = j;
  }

  for (i = 1; i <= aLength; i++) {
    for (j = 1; j <= bLength; j++) {
      const cost = a[i - 1] === b[j - 1] ? 0 : 1;

      d[i][j] = Math.min(
        d[i - 1][j] + 1,
        d[i][j - 1] + 1,
        d[i - 1][j - 1] + cost,
      );

      if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
        d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
      }
    }
  }

  return d[aLength][bLength];
}
function lexicalDistance ( aStr , bStr ) { if ( aStr === bStr ) { return 0 ; } let i ; let j ; const d = [ ] ; const a = aStr . toLowerCase ( ) ; const b = bStr . toLowerCase ( ) ; const aLength = a . length ; const bLength = b . length ; // Any case change counts as a single edit if ( a === b ) { return 1 ; } for ( i = 0 ; i <= aLength ; i ++ ) { d [ i ] = [ i ] ; } for ( j = 1 ; j <= bLength ; j ++ ) { d [ 0 ] [ j ] = j ; } for ( i = 1 ; i <= aLength ; i ++ ) { for ( j = 1 ; j <= bLength ; j ++ ) { const cost = a [ i - 1 ] === b [ j - 1 ] ? 0 : 1 ; d [ i ] [ j ] = Math . min ( d [ i - 1 ] [ j ] + 1 , d [ i ] [ j - 1 ] + 1 , d [ i - 1 ] [ j - 1 ] + cost , ) ; if ( i > 1 && j > 1 && a [ i - 1 ] === b [ j - 2 ] && a [ i - 2 ] === b [ j - 1 ] ) { d [ i ] [ j ] = Math . min ( d [ i ] [ j ] , d [ i - 2 ] [ j - 2 ] + cost ) ; } } } return d [ aLength ] [ bLength ] ; }
Computes the lexical distance between strings A and B.

The "distance" between two strings is given by counting the minimum number
of edits needed to transform string A into string B. An edit can be an
insertion, deletion, or substitution of a single character, or a swap of two
adjacent characters.

Includes a custom alteration from Damerau-Levenshtein to treat case changes
as a single edit which helps identify mis-cased values with an edit distance
of 1.

This distance can be useful for detecting typos in input or sorting

@param {string} a
@param {string} b
@return {int} distance in number of edits
Computes the lexical distance between strings A and B .