How can I check a word is made of letters in different array

Viewed 948

I am writing a word game in Vue.

You start with a string of random characters and you have to input a word that can be made from that string, eg for "ABEOHSTD" the user can enter "BASE" for a score of 4.

I have an external word list in a .txt file (which I also can't get working, but that's a separate issue) I need to verify the words against, but I'm having trouble verifying the words can be made from the given random string.

I don't know how I'd approach making sure each letter can only be used as many times as it appears in the array, or even store the scores but I just want to get this first part working.

I have tried splitting both the entered word and random string into an array of each character, looping through the array and checking if each character in the user entered string is included in the random string array.

      splitUserCurrentWord = this.userAttemptedWord.split("");
      for (var i = 0; i <= splitUserCurrentWord.length; i++) {
        if (this.randomStringForGame.split("").includes(splitUserCurrentWord[i])) {
          return true;
          //i++
        }
        else {
          return false;
        }
      }
    }

Currently I expect to resolve to true if all the letters in the user inputted word are present in the random string array, but it seems to only resolve to true or false based on the first letter of the array, which isn't good because as long as the first letter is in the random string array it will register as true/correct.

jsfiddle of the entire thing so far: https://jsfiddle.net/sk4f9d8w/

3 Answers

Your return statement is exiting the loop after the 1st iteration.

One way to do it is to use Array.every to verify all the letters and String.includes to check if the letter is part of the accepted String

const randomString = "ABEOHSTD";

console.log(isWordValid(randomString, "BASE"));
console.log(isWordValid(randomString, "BASEU"));

function isWordValid(validLetters, attemtedWord) {
  const attemptedWordSplitted = attemtedWord.split("");
  return attemptedWordSplitted.every(attemptedLetter => validLetters.includes(attemptedLetter));
}

If you don't allow to reuse the same letter multiple times, you need another approach by deleting the used letter from the list of acceptable letters

const randomString = "ABEOHSTD";

console.log(isWordValid(randomString, "BASE"));
console.log(isWordValid(randomString, "BAASE"));
console.log(isWordValid(randomString, "BASEU"));

function isWordValid(validLetters, attemptedWord) {
  const validLettersSplitted = validLetters.split("");
  const attemptedWordSplitted = attemptedWord.split("");
  return attemptedWordSplitted.every(attemptedLetter => {
    const letterIndex = validLettersSplitted.indexOf(attemptedLetter);
    if(letterIndex > -1){
      validLettersSplitted.splice(letterIndex, 1);
      return true;
    } else {
      return false
    }
  });
}

You are on the right way, You need to check each letter in the user word and check if they are in the random word. If one letter is in the random word, you remove it from the random word so it can't be use twice. :

let randomWord = "ABEOHSTD";
let userWordThatFail = "BAASE";
let userWord = "BASE";

// optionnaly, uppercase both words.

// we split into letters to make it easiers to process
let randomLetters = randomWord.split('');
let userLetters = userWord.split('');

let score = 0;

//we parse each letter of the user input
userLetters.forEach((letter) => {
  // the letter exists in the random word.
  let indexOfTheCurrentLetter = randomLetters.indexOf(letter);
  // the letter exists, we remove it and increment the score.
  if(indexOfTheCurrentLetter !== -1) {
    randomLetters.splice(indexOfTheCurrentLetter, 1);
    score++;
  }
});

// the user input contains letters that are not in the random input.
if(score < userLetters.length) {
  console.log('fail');
} else {
  console.log('win : ' + score);
}

A simple approach might be to iterate the list of valid characters for every character encountered in the string to test. Using string.includes would fall into this bracket. Problematically, that's O(n_validchars * n_testchars) time-complexity for each comparison. This might not be desirable for longer strings.

The JavaScript Set object can help here.

With this higher-order function (that leans heavily on the iterable nature of a string), you can generate a reusable function for a set of valid characters:

function testFor(validChars) {
  const charSet = new Set(validChars);
  return testString =>
    Array.prototype.every.call(testString, c => charSet.has(c));
}

// And to use it:

const testForABC = testFor("ABC"); //returns a function

console.log(testForABC("AABBCC")); //true
console.log(testForABC("abc")); //false

Now, because Set lookups are O(1), we're looking at O(n) complexity where n is the length of the string we're testing. Much better.

Related