mehran khalili
mehran khalili

Reputation: 11

How can I replace several letters in string in javascript?

I'm building a word game. One of the functions I need is a function that receives a word and replaces several words with (_) and gives the incomplete word as output. I want the number of words to be replaced and the location of those words to be chosen randomly. The code I wrote, although it uses for, is only able to replace one word. What changes should I make in the for part of the code that has the ability to move several words? for example : "laptop" => "l_pt_p"

    function wordToIncomplete(word) {
    let randomNumber = Math.floor(Math.random() * 3) + 1
    let _randomNumber = Math.floor(Math.random() * word.length)
    let _word = ""; 
    
    for (let index = 0; index < randomNumber; index++) {
        
        _word = word.replace(word[_randomNumber], '_');
        
    }
    return _word 
   }

Upvotes: 0

Views: 118

Answers (4)

zer00ne
zer00ne

Reputation: 43853

The example returns an array of letters which is easier to work with, but if for some reason you want a string, do the following to the last line:

return chars.join('');

Note: this example actually adjusts dynamically the number of letters to be replaced which is from 30% to 60% of word.length.

Details are commented in example below

// Utility function
const log = data => console.log(JSON.stringify(data));

// Random integer range function
const randRng = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;

const fragWord = word => {
  /*
  The result of the following expressions is a random number that's 
  within the range of 30% to 60% of total length of >word<
  >word< string is converted into an array of letters called >chars<
  */
  const size = word.length;
  const lo = Math.ceil(0.3 * size);
  const hi = Math.ceil(0.6 * size);
  let qty = randRng(lo, hi);
  let chars = word.split('');

  /*
  At each iteration of >chars< a random index number is generated. 
  If there's a '_' at that index, the current iteration is skipped
  and qty increases by 1 to compensate.
  Otherwise the letter at that index is replaced with a '_'
  */
  skip:
    for (let i = 0; i < qty; i++) {
      let index = randRng(0, size - 1);
      let char = chars.at(index);
      if (char === '_') {
        qty = qty + 1;
        continue skip;
      }
      chars[index] = '_';
    }
  return chars;
};

log(fragWord('fragment'));
log(fragWord('appearance'));
log(fragWord('institutionalization'));
log(fragWord('institutionalization'));

Upvotes: 1

Carsten Massmann
Carsten Massmann

Reputation: 28196

Here is my take on it:

function shfl(array) { // Durstenfeld shuffle, based on Fisher-Yates
  for (let i = array.length - 1; i > 0; i--) {
   const j = Math.floor(Math.random() * (i + 1));
   [array[i],array[j]]=[array[j],array[i]];
  }
  return array;
}
function getWd(word) {
  const n=~~(Math.random()*3)+1, // 1...3
   wa=word.split(""), // word as char-array
   pos=shfl(wa.map((_,i)=>i))     
      .slice(0,n); // 1...3 character positions to be replaced by _
  pos.forEach(p=>wa[p]="_")
  return wa.join("");
}

console.log(
 "And here comes my supercalifragilisticexpialidocious word I want to process".split(" ")
 .map(getWd).join(" ")
);

I use the Durstenfeld shuffle to pick out random character positions of each word.

In getWd() I split the word into an array wa. This array is then the base for finding 1..3 positions (pos) to be replaced by _. After replacing the positions I join("") the characters in wa again and return them as the changed word.

Another expression I'd like to explain is

~~(Math.random()*3)+1

The ~ operator ("bitwise negation") will implicitly convert any number into an integer (similar to Math.floor()). By applying it twice (~~) the actual bitwise negation will be reversed and the result is thus a shorthand for Math.floor().

Upvotes: 0

Nick Vu
Nick Vu

Reputation: 15510

I think your problem is you did not put your _randomNumber in the loop to re-generate your removed character index

function wordToIncomplete(word) {
  let randomNumber = Math.floor(Math.random() * 3) + 1
  let _word = word;

  for (let index = 0; index < randomNumber; index++) {
    let _randomNumber = Math.floor(Math.random() * _word.length)
    _word = _word.replace(_word[_randomNumber], '_');

  }
  return _word
}

const testingWords = "testing words"
console.log(wordToIncomplete(testingWords))

But with this solution, I think it will encounter another problem that it may check the same character which is already replaced earlier

Therefore, I modified your code a bit with while loop

function wordToIncomplete(word) {
  let randomNumber = Math.floor(Math.random() * 3) + 1
  let _word = word;

  let index = 0;
  while (index < randomNumber) {
    let _randomNumber = Math.floor(Math.random() * _word.length)
    if (_word[_randomNumber] === '_') { // we don't need to replace the checked words
      continue;
    }
    _word = _word.replace(_word[_randomNumber], '_');
    index++;
  }
  return _word
}

const testingWords = "testing words"
console.log(wordToIncomplete(testingWords))

By the way, you also did not assign value for _word, so I changed it to

let _word = word

Upvotes: 1

soc221b
soc221b

Reputation: 342

Yes, as @Barmar mentioned, naming is important.


What changes should I make in the for part of the code that has the ability to move several words

The problem here is that _randomNumber is always same during the loop.

function wordToIncomplete(word) {
  const randomNumberOfCharactersToBeReplaced =
    Math.floor(Math.random() * word.length) + 1;

  let result = word;
  for (let i = 0; i < randomNumberOfCharactersToBeReplaced; i++) {
    // suppose we don't care about whether the character is already replaced or not.
    const randomIndexToBeReplaced = Math.floor(Math.random() * word.length);

    result[randomIndexToBeReplaced] = "_";
  }
  return result;
}

Upvotes: 1

Related Questions