1

In the code that I develop and maintain I have ran into an issue.

I have a function that takes a query (type string) and replaces substrings of that string with a different string. As an example if a user types in the string I have a cat it would replace it with I have a dog.

My code works but the problem is I have hundreds of such substrings that need to be replaced with a different one. It also looks really bad aesthetically.

var myString;
myString = myString.replace('cat','dog')
                   .replace('elephant','zebra')
                   .replace('bird','fish')
                   // Goes on for hundreds of lines

This is all inside a function in which everytime it's called it goes through hundreds of replace calls.

One thing I could try doing is creating an array of objects and iterating through it. The code would look something like this.

var animalsArray = [
                       {'a':'cat','b':'dog'},
                       {'a':'elephant','b':'zebra'},
                       {'a':'bird','b':'fish'}
                   ];

And then in my function

function stringReplace(string) {
    for (var i = 0; i < animalsArray.length; i++) {
        if (string.indexOf(animalsArray[i]['a']) > -1) {
            sting = string.replace(animalsArray[i]['a'],animalsArray[i]['b']);
        }
    }
}

But I'm not sure whether that would improve on my current practice of chaining together hundreds of replace calls.

I'm basically looking to optimize my current code. What is the best practice?

0

3 Answers 3

2

You can make a regaulr expression with a bunch of or statements. (dog|elephant|bird|....) and that will allow you to run one check. The replace gives you the matched text which you can use to look up the word to replace.

So make an object of the strings to replace and their value is the word to replace. You can just look up the replacement by the matched key.

const animals = {
  cat: 'dog',
  elephant: 'zebra',
  bird: 'fish',
}

// build the or sting with a simple join
const keysString = Object.keys(animals).join("|")
// generate the regular expression to match the words
var animalRE = new RegExp(`\\b(${keysString})\\b`,'g');

// a sample string to match
const myString = "I like to pet a cat that looks like an elephant that moves like a bird."

// the replacement step. The function takes the matched text (aka key) and looks it up in the object
const updated = myString.replace(animalRE, key => animals[key] || key)

// display the new string
console.log(updated)

Sign up to request clarification or add additional context in comments.

1 Comment

Nice solution! As long as the words (or phrases) doesn't contain any special regex characters it will work fine. I did something similar a couple of months ago, and did some profiling comparing Object and Map, and a map was much faster (at least in Chrome and Node, the only environments I tested)
0

I'd consider using an object instead, whose keys are the substrings to be replaced, and whose values are the replacements. Make a regular expression by alternating all of the object's keys, then use a replacer function to look up the associated value on the object:

const replacements = {
  cat: 'dog',
  elephant: 'zebra',
  bird: 'fish'
};
const pattern = new RegExp(Object.keys(replacements).join('|'), 'g');
console.log('Foo cat bar bird'.replace(pattern, match => replacements[match]));

Using object syntax makes it easy to add/remove items. If you want to make it even easier to modify, you could consider putting the replacement information into a string instead, then parsing it into an object:

const replacementsStr = `
cat        dog
elephant   zebra
bird       fish
`;

const replacements = Object.fromEntries(
  replacementsStr
    .trim()
    .split('\n')
    .map(line => line.split(/\s+/))
);
const pattern = new RegExp(Object.keys(replacements).join('|'), 'g');
console.log('Foo cat bar bird'.replace(pattern, match => replacements[match]));

2 Comments

Obviously RegExp has been highly optimized in a browser but I wonder how a solution with indexOf and substring might perform against it albeit with more code. See also stackoverflow.com/questions/5296268/…
Using indexOf would require calling indexOf for every possible replacement, and for every possible character index combination for each of those possible replacements (eg, for 'cat', indicies 0-2, then indicies 1-3, then indicies 2-4, etc. If a match is found, do the same thing again, until no more matches are found. Then do the same thing for the next replacement, of which there are hundreds). Probably less efficient and more messy.
0

I would do something like:

function WordSwapper(sentence){
  const swapper = sentence;
  this.swaps = [];
  this.add = (word, replacer)=>{
    this.swaps.push([word, replacer]);
    return this;
  }
  this.swap = (sentence = null)=>{
    let s = sentence === null ? swapper : sentence;
    this.swaps.forEach(a=>{
      s = s.replace(new RegExp(a[0], 'gi'), a[1]);
    });
    return s;
  }
}
const ws = new WordSwapper('The cat plays. Elephants live in the Serengeti. Have you ever seen a bird fly?');
ws.add('cat', 'dog').add('elephant', 'zebra').add('bird', 'fish');
console.log(ws.swap());

Of course, you may want to do something with plural and uppercase situations.

1 Comment

I really appreciate the response. Upper Case and plurals would be pretty easy but that's outside the scope of this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.