127

The Javascript splice only works with arrays. Is there similar method for strings? Or should I create my own custom function?

The substr(), and substring() methods will only return the extracted string and not modify the original string. What I want to do is remove some part from my string and apply the change to the original string. Moreover, the method replace() will not work in my case because I want to remove parts starting from an index and ending at some other index, exactly like what I can do with the splice() method. I tried converting my string to an array, but this is not a neat method.

Louis
  • 146,715
  • 28
  • 274
  • 320
ProllyGeek
  • 15,517
  • 9
  • 53
  • 72

10 Answers10

110

It is faster to slice the string twice, like this:

function spliceSlice(str, index, count, add) {
  // We cannot pass negative indexes directly to the 2nd slicing operation.
  if (index < 0) {
    index = str.length + index;
    if (index < 0) {
      index = 0;
    }
  }

  return str.slice(0, index) + (add || "") + str.slice(index + count);
}

than using a split followed by a join (Kumar Harsh's method), like this:

function spliceSplit(str, index, count, add) {
  var ar = str.split('');
  ar.splice(index, count, add);
  return ar.join('');
}

Here's a jsperf that compares the two and a couple other methods. (jsperf has been down for a few months now. Please suggest alternatives in comments.)

Although the code above implements functions that reproduce the general functionality of splice, optimizing the code for the case presented by the asker (that is, adding nothing to the modified string) does not change the relative performance of the various methods.

Lorenz Meyer
  • 19,166
  • 22
  • 75
  • 121
Louis
  • 146,715
  • 28
  • 274
  • 320
  • 3
    You can add this globally with: ```String.prototype.splice = function (index, count, add) { return this.slice(0, index) + (add || "") + this.slice(index + count); }``` Just keep in mind it doesn't work exactly the same as the array splice method because strings are immutable (you can't modify them once created). So usage would be like: ```var s="ss"; s = s.splice(0,1,"t");``` – William Neely Nov 18 '14 at 16:52
  • @WilliamNeely: `var StringUtils={'StringSplice':/*func def*/};` would be standard for string manipulation as you have said that it is immutable. – Mr. Polywhirl Nov 25 '14 at 16:37
  • spliceSlice and spliceSplit are not functionally equal due to Array.prototype.splice accepting negative indices: https://jsfiddle.net/sykteho6/5/. – Martijn Jul 04 '16 at 13:35
  • 1
    @Mzialla Thanks, I've added code to take care of it. – Louis Jul 04 '16 at 14:13
  • 2
    Creating temp vars and introducing places for off-by-one errors, not to mention code that takes longer for humans to process as opposed to split("").splice(...).join("") is a tradeoff that deserves consideration. The speed issue is only an issue if it's an issue. – ballenf Oct 14 '16 at 06:04
  • `str.split()` can also take a pattern, potentially skipping the need for `indexOf()`, so the jsperf comparison isn't necessarily true for all use cases – Atav32 Jan 18 '18 at 20:38
  • Add `substring` test case in the jsperf https://jsperf.com/split-or-slice-for-string-splice/12, which is 35% faster than `slice` on my Chrome (79.0.3945.88), I don't know if it is related to test case order – alphakevin Dec 26 '19 at 02:33
  • @Louis Here is a benchmark link : http://jsbench.github.io/#bacf7a0626e278bed73e65c6f45c50b5 – astroide Jun 18 '21 at 22:06
  • Since you asked for suggestions here's another one: [jsbench.me](https://jsbench.me) – Capt 171 Oct 22 '21 at 05:46
19

Edit

This is of course not the best way to "splice" a string, I had given this as an example of how the implementation would be, which is flawed and very evident from a split(), splice() and join(). For a far better implementation, see Louis's method.


No, there is no such thing as a String.splice, but you can try this:

newStr = str.split(''); // or newStr = [...str];
newStr.splice(2,5);
newStr = newStr.join('');

I realise there is no splice function as in Arrays, so you have to convert the string into an array. Hard luck...

Community
  • 1
  • 1
kumarharsh
  • 18,961
  • 8
  • 72
  • 100
  • in my case im working on a dynamic string so i need to specify indices and not character value . – ProllyGeek Dec 28 '13 at 18:11
  • Yes, that is unfortunately correct. But you can get the index of a character by using String.prototype.indexOf as `"asdsd".indexOf('s');` – kumarharsh Dec 28 '13 at 18:14
  • 3
    Instead of using str.split, you can use ES6 spread syntax like so: `[...str]` – robbie Apr 26 '17 at 14:01
  • very inefficient/slow/amateurish, you just concatenate the part before and after the part to drop instead! – Andris Feb 13 '22 at 20:35
7

There seem to be a lot of confusion which was addressed only in comments by elclanrs and raina77ow, so let me post a clarifying answer.

Clarification

From "string.splice" one may expect that it, like the one for arrays:

  • accepts up to 3 arguments: start position, length and (optionally) insertion (string)
  • returns the cut out part
  • modifies the original string

The problem is, the 3d requirement can not be fulfilled because strings are immutable (related: 1, 2), I've found the most dedicated comment here:

In JavaScript strings are primitive value types and not objects (spec). In fact, as of ES5, they're one of the only 5 value types alongside null, undefined, number and boolean. Strings are assigned by value and not by reference and are passed as such. Thus, strings are not just immutable, they are a value. Changing the string "hello" to be "world" is like deciding that from now on the number 3 is the number 4... it makes no sense.

So, with that in account, one may expect the "string.splice" thing to only:

  • accepts up to 2 arguments: start position, length (insertion makes no sense since the string is not changed)
  • returns the cut out part

which is what substr does; or, alternatively,

  • accepts up to 3 arguments: start position, length and (optionally) insertion (string)
  • returns the modified string (without the cut part and with insertion)

which is the subject of the next section.

Solutions

If you care about optimizing, you should probably use the Mike's implementation:

String.prototype.splice = function(index, count, add) {
    if (index < 0) {
        index += this.length;
        if (index < 0)
            index = 0;
    }
    return this.slice(0, index) + (add || "") + this.slice(index + count);
}

Treating the out-of-boundaries index may vary, though. Depending on your needs, you may want:

    if (index < 0) {
        index += this.length;
        if (index < 0)
            index = 0;
    }
    if (index >= this.length) {
        index -= this.length;
        if (index >= this.length)
            index = this.length - 1;
    }

or even

    index = index % this.length;
    if (index < 0)
        index = this.length + index;

If you don't care about performance, you may want to adapt Kumar's suggestion which is more straight-forward:

String.prototype.splice = function(index, count, add) {
    var chars = this.split('');
    chars.splice(index, count, add);
    return chars.join('');
}

Performance

The difference in performances increases drastically with the length of the string. jsperf shows, that for strings with the length of 10 the latter solution (splitting & joining) is twice slower than the former solution (using slice), for 100-letter strings it's x5 and for 1000-letter strings it's x50, in Ops/sec it's:

                      10 letters   100 letters   1000 letters
slice implementation    1.25 M       2.00 M         1.91 M
split implementation    0.63 M       0.22 M         0.04 M

note that I've changed the 1st and 2d arguments when moving from 10 letters to 100 letters (still I'm surprised that the test for 100 letters runs faster than that for 10 letters).

YakovL
  • 7,557
  • 12
  • 62
  • 102
6

Here's a nice little Curry which lends better readability (IMHO):

The second function's signature is identical to the Array.prototype.splice method.

function mutate(s) {
    return function splice() {
        var a = s.split('');
        Array.prototype.splice.apply(a, arguments);
        return a.join('');
    };
}

mutate('101')(1, 1, '1');

I know there's already an accepted answer, but hope this is useful.

Cody
  • 9,785
  • 4
  • 61
  • 46
  • 1
    Np. You can even abstract this to allow, say, an optional `'method'` parameter so you can pass in an `Array.prototype[method]` and treat strings completely like arrays. Also, with this one alone, you could abstract the actual process to another function -- and if there's no string, return a function that will take the string and reverse the curry: `mutate()(1, 1, '1')('101')`; You could even duckType the arguments to rid the curry of the empty invocation -- even encapsulate a method, itself. If you need this stuff, you may want to look into the *Command Pattern* though. Just spit-balling here. – Cody Aug 16 '16 at 20:03
  • very nice way to do it! – Olga Farber Mar 16 '21 at 21:58
3

I would like to offer a simpler alternative to both the Kumar/Cody and the Louis methods. On all the tests I ran, it performs as fast as the Louis method (see fiddle tests for benchmarks).

String.prototype.splice = function(startIndex,length,insertString){
    return this.substring(0,startIndex) + insertString + this.substring(startIndex + length);
};

You can use it like this:

var creditCardNumber = "5500000000000004";
var cardSuffix = creditCardNumber.splice(0,12,'****');
console.log(cardSuffix);  // output: ****0004

See Test Results: https://jsfiddle.net/0quz9q9m/5/

Timothy Kanski
  • 1,861
  • 14
  • 20
  • you should at least do ` + (insertString || "") + ` instead of ` + insertString + `, otherwise the third argument is not optional. This implementation also doesn't take care of `startIndex < 0` unlike other suggestions. Great example of usage, though – YakovL Nov 11 '18 at 10:37
2

Simply use substr for string

ex.

var str = "Hello world!";
var res = str.substr(1, str.length);

Result = ello world!

Dinesh Patil
  • 1,042
  • 10
  • 13
0

The method Louis's answer, as a String prototype function:

String.prototype.splice = function(index, count, add) {
  if (index < 0) {
    index = this.length + index;
    if (index < 0) {
      index = 0;
    }
  }
  return this.slice(0, index) + (add || "") + this.slice(index + count);
}

Example:

 > "Held!".splice(3,0,"lo Worl")
 < "Hello World!"
Community
  • 1
  • 1
Mike Godin
  • 3,727
  • 3
  • 27
  • 29
0

So, whatever adding splice method to a String prototype cant work transparent to spec...

Let's do some one extend it:

String.prototype.splice = function(...a){
    for(var r = '', p = 0, i = 1; i < a.length; i+=3)
        r+= this.slice(p, p=a[i-1]) + (a[i+1]||'') + this.slice(p+a[i], p=a[i+2]||this.length);
    return r;
}
  • Every 3 args group "inserting" in splice style.
  • Special if there is more then one 3 args group, the end off each cut will be the start of next.
    • '0123456789'.splice(4,1,'fourth',8,1,'eighth'); //return '0123fourth567eighth9'
  • You can drop or zeroing the last arg in each group (that treated as "nothing to insert")
    • '0123456789'.splice(-4,2); //return '0123459'
  • You can drop all except 1st arg in last group (that treated as "cut all after 1st arg position")
    • '0123456789'.splice(0,2,null,3,1,null,5,2,'/',8); //return '24/7'
  • if You pass multiple, you MUST check the sort of the positions left-to-right order by youreself!
  • And if you dont want you MUST DO NOT use it like this:
    • '0123456789'.splice(4,-3,'what?'); //return "0123what?123456789"
0

Louis's spliceSlice method fails when add value is 0 or other falsy values, here is a fix:

function spliceSlice(str, index, count, add) {
  if (index < 0) {
    index = str.length + index;
    if (index < 0) {
      index = 0;
    }
  }
  const hasAdd = typeof add !== 'undefined';
  return str.slice(0, index) + (hasAdd ? add : '') + str.slice(index + count);
}
0

I solved my problem using this code, is a somewhat replacement for the missing splice.

let str = "I need to remove a character from this";
let pos = str.indexOf("character")

if(pos>-1){
  let result = str.slice(0, pos-2) + str.slice(pos, str.length);
  console.log(result) //I need to remove character from this 
}

I needed to remove a character before/after a certain word, after you get the position the string is split in two and then recomposed by flexibly removing characters using an offset along pos