Is there a splice method for strings?

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).


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.


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...


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.

Tags:

Javascript