7
\$\begingroup\$

I completed this challenge Search and Replace and passed the tests:

Perform a search and replace on the sentence using the arguments provided and return the new sentence.

First argument is the sentence to perform the search and replace on.

Second argument is the word that you will be replacing (before).

Third argument is what you will be replacing the second argument with (after).

Note

Preserve the case of the first character in the original word when you are replacing it. For example if you mean to replace the word "Book" with the word "dog", it should be replaced as "Dog".

Example

myReplace("Let us go to the store", "store", "mall") should return "Let us go to the mall".

I was hoping to simplify my code. Is there a more concise way to write this program that would end up being cleaner and faster than the current code?

function myReplace(str, before, after) {
  let newStr = str.split(' ');
  for (var a=0; a < str.length; a++) {
    if(before === newStr[a]) {
      str = str.replace(before, after);
    } 
    if (before[0] === before[0].toUpperCase()) {
      var swap = after[0].toUpperCase() + after.slice(1);
      str = str.replace(before, swap)
    }
  }
  return str;
}

console.log(myReplace("A quick brown fox jumped over the lazy dog", "jumped", "leaped"));

\$\endgroup\$
1
  • 1
    \$\begingroup\$ How is "cleaner" defined and "faster" measured? \$\endgroup\$ Commented Dec 1, 2018 at 0:56

2 Answers 2

1
\$\begingroup\$

This kind of test boils down the the familiarity of built-in JavaScript APIs. The naive way to go about this problem is to scan through the string and do the checks manually. However...

Pass a function as second argument of string.replace() and it will call that function for each match it encounters. The return value of this function becomes the replacement. So instead of manually scanning the string, you can let string.replace() do that heavy-lifting. Note that passing a string as first argument to replace only makes it run once, which is why the first argument is a RegExp with a g flag constructed from the string you want replaced.

Also, a minor suggestion. Instead of comparing the first letter with its upper case version to see if it is upper case, you can use regex.test() to see if a character matches a pattern that only matches uppercase. It's a bit shorter, if length is what you're after.

function myReplace(str, before, after) {
  return str.replace(new RegExp(before, 'g'), match => {
    return (/[A-Z]/).test(before[0]) ? `${after[0].toUpperCase()}${after.slice(1)}` : after
  })
}

console.log(myReplace("Let us go to the store", "store", "mall"))
console.log(myReplace("He is Sleeping on the couch", "Sleeping", "sitting"))
console.log(myReplace("This has a spellngi error", "spellngi", "spelling"))
console.log(myReplace("His name is Tom", "Tom", "john"))
console.log(myReplace("Let us get back to more Coding", "Coding", "algorithms"))
console.log(myReplace("foo foo foo foo bar foo", "foo", "baz"))

\$\endgroup\$
1
  • 5
    \$\begingroup\$ The regexp should be escaped and case-insensitive. Since the challenge specifies that before is a "word", it should also have \b assertions at the beginning and end. \$\endgroup\$ Commented Aug 2, 2018 at 21:22
1
\$\begingroup\$

As Joseph's answer illustrates a regular expression could be used to eliminate the for loop, though it would need to be escaped and surrounded by word boundary meta characters1.

The current code has a for loop which iterates from zero to one less than the length of the input string str. In the example code

console.log(myReplace("A quick brown fox jumped over the lazy dog", "jumped", "leaped"));

That string has 42 characters so the for loop iterates from zero to 41. The first if checks before === newStr[a]. In that example string, newStr is an array with nine elements so after a has a value of 10 or more it is comparing before with undefined, which will never evaluate to true.

The second conditional block, which starts with:

if (before[0] === before[0].toUpperCase()) {

is evaluated on every iteration of the loop, so if before starts with an uppercase character, it that conditional will always evaluate to true. This leads to str.replace() being called on every iteration of the loop regardless of whether the previous block was executed. That whole block could be moved before the for loop and used to assign a value to the string used in the call to str.replace() later in the loop.

The code also uses let for newStr and var for swap. Both variables are never re-assigned so const can be used. This helps avoid accidental re-assignment if and when code is expanded.

The for loop could be replaced with a for...of Loop which would simplify the syntax- especially eliminating the need to increment the counter variable a for dereferencing the string characters.

The name newStr would be better called words since it is an array of word.

Putting the advice above together gives code like this:

function myReplace(str, before, after) {
  const words = str.split(' ');
  let newSubstr = after;
  if (before[0] === before[0].toUpperCase()) {
    newSubstr = after[0].toUpperCase() + after.slice(1);
  }
  for (word of words) {
    if (word === before) {
      str = str.replace(before, newSubstr);
    }
  }
  return str;
}
console.time && console.time('replace');
const replacedStr = myReplace("A quick brown fox Jumped over the lazy dog", "Jumped", "leaped");
console.timeEnd && console.timeEnd('replace');
console.log('replaced string: ', replacedStr);

Since the string gets split into an array, that array could be used to hold the sub-strings with replacements and then joined after the loop. This does seem slightly slower compared to the previous example.

function myReplace(str, before, after) {
  const words = str.split(' ');
  let newSubstr = after;
  if (before[0] === before[0].toUpperCase()) {
    newSubstr = after[0].toUpperCase() + after.slice(1);
  }
  for ([index, word] of words.entries()) {
    if (word === before) {
      words.splice(index, 1, newSubstr)
    } 
  }
  return words.join(' ');
}
console.time && console.time('replace');
const replacedStr = myReplace("A quick brown fox Jumped over the lazy dog", "Jumped", "leaped");
console.timeEnd && console.timeEnd('replace');
console.log('replaced string: ', replacedStr);

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.