简体   繁体   中英

Find all occurrences (letters) in array (word)

I want to create a hangman game in React.js, with this code when the user click on a letter, it will search the letter in the word and display her. It work correctly when the word contain only one same letter. I would like this code work with word like 'PROGRAMMING' with 2 'M'.

handleChooseLetter = (index) => {

const usedWord = [...this.state.usedWord];

const chosenLetter = this.state.letters[index].letter;

var letterPosition = usedWord.indexOf(chosenLetter);

if (letterPosition >= 0) {
   hiddenWord.splice(letterPosition, 1, chosenLetter); 

   this.setState({hiddenWord: hiddenWord});
}

}

I already try a while loop but it not work in my case:

var indices = [];

while(letterPosition >= 0) {
  const hiddenWord = [...this.state.hiddenWord];
  indices.push(letterPosition);
  letterPosition = usedWord.indexOf(chosenLetter, letterPosition + 1);
  hiddenWord.splice(letterPosition, 1, chosenLetter); 
  this.setState({hiddenWord: hiddenWord});
}

For me, the result is that find the letter and display them always for the last letter of the word.

I think my problem is with the splice method who splice the wrong letterPosition

Here my chooseWord function:

state = {
wordList: [
  { id: 1, word: 'PROGRAMMING'},
],
usedWord: [],
hiddenWord: [],
}

chooseWord() {
const wordList = [...this.state.wordList];

const listLength = wordList.length;

const randomWord = this.state.wordList[Math.floor(Math.random() * listLength)].word;

const splittedWord = randomWord.split("");

const arr = new Array(randomWord.length + 1).join("_").split("");

this.setState({
  usedWord: splittedWord, 
  hiddenWord: arr
});

}

The simplest way is replace , not using an array:

 const usedWord = "programming"; const chosenLetter = "m"; const hiddenWord = usedWord.replace(new RegExp("[^" + chosenLetter + "]", "g"), "_"); console.log(hiddenWord); 

As the user adds more letters, you can add them to the character class:

 const usedWord = "programming"; const chosenLetters = "mp"; const hiddenWord = usedWord.replace(new RegExp("[^" + chosenLetters + "]", "g"), "_"); console.log(hiddenWord); 

React Example:

 class Hangman extends React.Component { constructor(...args) { super(...args); this.state = { availableLetters: "abcdefghijklmnopqrstuvwxyz", chosenLetters: "", word: this.props.word }; this.chooseLetter = this.chooseLetter.bind(this); } chooseLetter({target: {tagName, type, value}}) { if (tagName === "INPUT" && type === "button") { this.setState(prevState => ({chosenLetters: prevState.chosenLetters + value})); } } render() { const {word, chosenLetters} = this.state; const hiddenWord = word.replace(new RegExp("[^" + chosenLetters + "]", "g"), "_"); return <div> <div>Word:&nbsp;&nbsp;&nbsp;<span className="hiddenWord">{hiddenWord}</span></div> <div onClick={this.chooseLetter} style={{marginTop: "8px"}}> {[...this.state.availableLetters].map( letter => <input type="button" value={letter} disabled={chosenLetters.includes(letter)} /> )} </div> </div>; } } ReactDOM.render( <Hangman word="programming" />, document.getElementById("root") ); 
 .hiddenWord { font-family: monospace; letter-spacing: 1em; font-size: 18px; } 
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> 


For single English alphabet letters, you don't have to worry about using new RegExp(chosenLetter, "g") because none of the English alphabetic letters has special meaning in a regular expression. If you did have characters with special meaning ( . , $ , etc.), you'd escape the character before passing it to the RegExp constructor; see this question's answers for ways to do that.

I've added letter input <input onChange={this.handleChooseLetter} value={letter} /> and changed your handleChooseLetter function to iterate through letters of used word if at least 1 letter is found (because your usedWord.indexOf(chosenLetter) always returns 1 index only), so I decided to iterate entire word and check for chosen letter, if letter on that index exists, I just insert that letter to the hidden word on the same index - because hidden and used words have the same length:

 class App extends React.Component { constructor(props) { super(props); this.state = { hiddenWord: "___________", usedWord: "PROGRAMMING", letter: "" }; } handleChooseLetter = e => { const usedWord = [...this.state.usedWord]; const chosenLetter = e.target.value.toLocaleUpperCase(); let letterPosition = usedWord.indexOf(chosenLetter); if (letterPosition > -1) { this.setState(prevState => { const hiddenWord = [...prevState.hiddenWord]; for (let i = 0; i < usedWord.length; i++) { if (usedWord[i] === chosenLetter) { hiddenWord[i] = chosenLetter; } } return { hiddenWord, letter: "" }; }); return; } this.setState({ letter: "" }); }; render() { const { hiddenWord, letter } = this.state; return ( <div className="App"> {[...hiddenWord].map((letter, index) => ( <span key={index}>{letter}&nbsp;</span> ))} <input onChange={this.handleChooseLetter} value={letter} /> </div> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.1/umd/react-dom.production.min.js"></script> <div id="root"></div> 

Aha! I had to do a similar project for my class once. Heres the github repo for your reference: https://github.com/LordKriegan/WraithFood

Essentially, what I did was I created an object with several strings and a few functions. When the game loads, it picks a random word from my list and sets 2 of the strings. One is the full word (lets call this property fullWord), the other is basically a string of the same length with all letters converted to underscores (let's call this one guessedWord, also see setWord() in the game.js file).

But you are interested in checking the letters! Thats easy enough. Javascript strings have a built-in method called .includes(), which takes a string as a parameter. Since I had my word saved in the object, I simply ran a .includes() on that word with the letter I wanted to check, then if it passed all my validation (letter already guessed, whether it is or isnt in the word, etc), I ran a for loop on guessedWord with another String method called .charAt(i). This method simply returns the character at position i in the string its called on. Since I have to strings in my object, which I KNOW to be the same length, I can execute a for loop on fullWord, check every letter at position i, and then reset the guessedWord property with .substr(). This method returns partial strings based on what parameters you pass it. Essentially I set guessedWord to + + . This was the checkLet() function in the game object.

I would reccomend reading up on string methods at https://www.w3schools.com/js/js_string_methods.asp

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM