# Search and Replace (FreeCodeCamp intermediate algorithm scripting)

Posted on

Problem

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"));``````

Solution

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"))``````

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);``````