Home > Back-end >  how to build a function that can find a list of words in an array even if I spell the words incorrec
how to build a function that can find a list of words in an array even if I spell the words incorrec

Time:12-29

for example:

const arrayOfLists = ['fox', 'ant', 'bird', 'lion', 'wolf', 'deer', 'bear', 'frog', 'hen', 'mole', 'duck', 'goat', 'dog', 'cat', 'bat', 'rooster', 'cow'];

function find(word) {
  "I'm not sure about this part"
}

const result = find('dgocat')

console.log(result)

it should return me ['goat', 'dog', 'cat'] in the console

CodePudding user response:

The strategy strictly depends on the criteria chosen to determine how a word should match the allowed words in the array.

Now given the requirements like:

'dgocat' should match ['goat', 'dog', 'cat']

It seems to me a good criteria is: if the input word contains all the letters contained in a word in the list, the target will match.

This is achieved with the functions countLetters(word) and compareLettersCount(word, allowedWord) that respectively do count the letters in a word and compare that results coming from two different words.

The comparison works checking if for each letter in the allowed word the letter count of the input word for that letter is equal or greater (also greater because the input word may contain several times the same letter like dogg but it still should match with dog).

const arrayOfLists = ['fox', 'ant', 'bird', 'lion', 'wolf', 'deer', 'bear', 'frog', 'hen', 'mole', 'duck', 'goat', 'dog', 'cat', 'bat', 'rooster', 'cow'];

/**
 * Returns an array with the allowed words that the input word tried to target
 */
function find(word) {
  const matchingWords = [];
  //for each word in arrayOfLists
  for(let i=0;i<arrayOfLists.length;i  ){
    //current allowed word
    const allowedWord = arrayOfLists[i];
    //gets the letters count for the input word
    const lettersCountWord = countLetters(word);
    //gets the letters count for the allowed word
    const lettersCountAllowedWord = countLetters(allowedWord);
    //if the input word has the same letters count (or greater) for the letters in allowedWord 
    if ( compareLettersCount(lettersCountWord, lettersCountAllowedWord) ){
      //push this allowedWord in the returning array
      matchingWords.push(allowedWord)
      //break;
    }
  }
  return matchingWords;
}

/**
 * Returns an object with the count of each letter in word
 */
function countLetters(word){
  const r = {};
  [...word].forEach(letter => {
    if(!r[letter])
      r[letter] = 1;
    else
      r[letter]  ;
  });
  return r;
}

/**
 * Compares the letterCount returned from word and allowedWord
 * return true if word contains the same counters (or greater) for the letters in allowedWord
 * otherwise returns false
 */
function compareLettersCount(word, allowedWord){
  for(let letter in allowedWord){
    if(word[letter] === undefined || word[letter] < allowedWord[letter])
      return false;
  }
  return true;
}

const result = find('dgocat')

console.log(result)

CodePudding user response:

A other way is to use the levenshtein algorithm. Here is a wiki. It calculates the minimum number of single-character edits (insertions, deletions or substitutions).

function levenshtein() {
  function _min(d0, d1, d2, bx, ay) {
    return d0 < d1 || d2 < d1
      ? d0 > d2
        ? d2   1
        : d0   1
      : bx === ay
      ? d1
      : d1   1;
  }

  return function (a, b) {
    if (a === b) {
      return 0;
    }

    if (a.length > b.length) {
      var tmp = a;
      a = b;
      b = tmp;
    }

    var la = a.length;
    var lb = b.length;

    while (la > 0 && a.charCodeAt(la - 1) === b.charCodeAt(lb - 1)) {
      la--;
      lb--;
    }

    var offset = 0;

    while (offset < la && a.charCodeAt(offset) === b.charCodeAt(offset)) {
      offset  ;
    }

    la -= offset;
    lb -= offset;

    if (la === 0 || lb < 3) {
      return lb;
    }

    var x = 0;
    var y;
    var d0;
    var d1;
    var d2;
    var d3;
    var dd;
    var dy;
    var ay;
    var bx0;
    var bx1;
    var bx2;
    var bx3;

    var vector = [];

    for (y = 0; y < la; y  ) {
      vector.push(y   1);
      vector.push(a.charCodeAt(offset   y));
    }

    var len = vector.length - 1;

    for (; x < lb - 3; ) {
      bx0 = b.charCodeAt(offset   (d0 = x));
      bx1 = b.charCodeAt(offset   (d1 = x   1));
      bx2 = b.charCodeAt(offset   (d2 = x   2));
      bx3 = b.charCodeAt(offset   (d3 = x   3));
      dd = x  = 4;
      for (y = 0; y < len; y  = 2) {
        dy = vector[y];
        ay = vector[y   1];
        d0 = _min(dy, d0, d1, bx0, ay);
        d1 = _min(d0, d1, d2, bx1, ay);
        d2 = _min(d1, d2, d3, bx2, ay);
        dd = _min(d2, d3, dd, bx3, ay);
        vector[y] = dd;
        d3 = d2;
        d2 = d1;
        d1 = d0;
        d0 = dy;
      }
    }

    for (; x < lb; ) {
      bx0 = b.charCodeAt(offset   (d0 = x));
      dd =   x;
      for (y = 0; y < len; y  = 2) {
        dy = vector[y];
        vector[y] = dd = _min(dy, d0, dd, bx0, vector[y   1]);
        d0 = dy;
      }
    }

    return dd;
  };
}

To use it:

function findWord(word) {
  arrayOfLists.filter(f => levenshtein()(word, f) <= 3)
}

The number 3 means it need 3 steps to until both words agree. Sample: 1: delete one letter, 2: add one letter,... and so on. With this number you can play and fine tune your function.

Here is a Stackblitz.

CodePudding user response:

You can use simple logic:

  • Loop on arrayOfLists
  • For every word, split and check if every character is in password word.
  • Return list of matching word

const arrayOfLists = ['fox', 'ant', 'bird', 'lion', 'wolf', 'deer', 'bear', 'frog', 'hen', 'mole', 'duck', 'goat', 'dog', 'cat', 'bat', 'rooster', 'cow'];

function find(word) {
  return arrayOfLists.filter(
    (str) => [...str].every(
      (char) => word.includes(char)
    )
  )
}

const result = find('dgocat')

console.log(result)

  • Related