How to replace all occurrences of a string except the first one in JavaScript?
Asked Answered
L

6

15

I have this string:

hello world hello world hello world hello

and I need to get the following:

hello world hello hello hello

If I use:

str = str.replace('world', '');

it only removes the first occurrence of world in the above string.

How can I replace all the occurrences of it except the first one?

Landowner answered 24/4, 2018 at 23:31 Comment(2)
@Legman That is not an accurate duplicate. The OP isn't asking about all instances.Abut
By use of a capture group: str.replace(/^(.*?world)|world/g, '$1') (explanation)Unpractical
B
20

You can pass a function to String#replace, where you can specify to omit replacing the first occurrence. Also make your first parameter of replace a regex to match all occurrences.

Demo

let str = 'hello world hello world hello world hello',
    i = 0;
    
str = str.replace(/world/g, m  => !i++ ? m : '');
console.log(str);

Note

You could avoid using the global counter variable i by using a IIFE:

let str = 'hello world hello world hello world hello';

str = str.replace(/world/g, (i => m => !i++ ? m : '')(0));
console.log(str);
Boatel answered 24/4, 2018 at 23:44 Comment(3)
You don't need to use regex for simple cases as in the question, you can match a string with replaceAll: str.replaceAll('world', m => !i++ ? m : '');Reluctivity
just bear in mind that replaceAll is not nativelly supported in nodeJS version < 15. See developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Boatel
That's true. For future readers: you can use replaceAll in some situations and add a polyfill if needed, as I stated in that answer: Jest: TypeError: replaceAll is not a functionReluctivity
C
3

To provide an alternative to @Kristianmitk excellent answer, we can use a positive lookbehind, which is supported in Node.Js & Chrome >= 62

const string = 'hello world hello world hello world hello';

console.log(
  string.replace(/(?<=world[\s\S]+)world/g, '')
);
// or
console.log(
  string.replace(/(?<=(world)[\s\S]+)\1/g, '')
);

Using Symbol.replace well-known symbol.

The Symbol.replace well-known symbol specifies the method that replaces matched substrings of a string. This function is called by the String.prototype.replace() method.

const string = 'hello world hello world hello world hello';

class ReplaceButFirst {
     constructor(word, replace = '') {
         this.count = 0;
         this.replace = replace;
         this.pattern = new RegExp(word, 'g');
     }
     
     [Symbol.replace](str) {
          return str.replace(this.pattern, m => !this.count++ ? m : this.replace);
     }
}

console.log(
  string.replace(new ReplaceButFirst('world'))
);
Coz answered 25/4, 2018 at 0:9 Comment(1)
This is also a very slick answer! Probs better for a Node script than a browser script.Rupert
M
1

In my solution, I am replacing first occurrence with a current timestamp, then replacing all occurrences and then finally replacing timestamp with world

You can also use str.split('world') and then join

var str = 'hello world hello world hello world hello';
var strs = str.split('world');
str = strs[0] + 'world' + strs.slice(1).join('');
console.log(str);

var str = 'hello world hello world hello world hello';

const d = Date.now()
str = str.replace('world', d).replace(/world/gi, '').replace(d, 'world');

console.log(str);
Merlon answered 24/4, 2018 at 23:38 Comment(1)
what happens if the timestamp itself a word in that sentence?Gerundive
G
1

var str = 'hello world hello world hello world hello'; 

var count = 0;
var result = str.replace(/world/gi, function (x) {
  if(count == 0) {
      count++;
      return x;
    } else {
      return '';	
    }
});

console.log(result);
Gerundive answered 24/4, 2018 at 23:45 Comment(0)
U
1

Without additional function or lookarounds it can be done by using a variation of The Trick.

let str = 'hello world hello world hello world hello';

str = str.replace(/^(.*?world)|world/g, '$1');
console.log(str);

See this regex101 demo

The part from ^ start until the first occurance of world (lazy .*? between) gets captured by group one | OR any remaining worlds are normally matched. In this case the "trick" is to capture what is needed on the left side of the alternation | but match what is not needed on the right.

Replacement is $1 and contains the substing until the first world if the left side succeeded. Else if the right side matched, the first group would be empty - hence world gets removed.


Since nowadays lookbehind is available in JS regex, another idea is to replace (world)(?<=\1.+) with empty. It checks if the capture occurs before in the string and is not necessarily more efficient.

Unpractical answered 15/6 at 10:46 Comment(0)
F
0

I would do it like this:

  1. Get the substring up until and including the first occurrence.
  2. Append to that the substring after the first occurrence, with all other occurrences removed:

    function replaceExceptFirst(str, search) {
         let index = str.indexOf(search);
         return str.substring(0, index + search.length) + 
             str.substring(index + search.length).replace(/world/g, '')
    }
    
    console.log(replaceExceptFirst('hello world hello world world hello', 'world'))
Frisch answered 24/4, 2018 at 23:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.