Add `.splice` to `String.prototype`

Proposal: GitHub - zion-off/proposal-string-splice: A TC39 proposal to add .splice() to String.prototype

Overview

In JavaScript, String.prototype and Array.prototype share a good few

methods. However, String.prototype is missing .splice(), which happens to be

quite useful when working with arrays. Currently, the method can be simulated

when working with strings in a few different ways.

Examples

Consider the following example, where I want add the character c to myString

at the second index.


let myString = "abde";

// using `String.prototype.slice`

let firstHalf = myString.slice(0, 2);

let secondHalf = myString.slice(2);

myString = firstHalf + "c" + secondHalf;

// using `Array.prototype` methods

myString.split("").splice(2, 0, "c").join("");

Both of these methods get the job done, but are unnecessarily verbose. With a pure function such as String.prototype.splice, we can achieve the same effect far more easily.


myString.splice(2, 0, "c");

Specification

Ecmarkup source

HTML

Acknowledgements

Although a similar proposal exists here, this propsal maintains consistency in nomenclature between Array.prototype and String.prototype methods.

1 Like

On arrays, splice mutates, and strings are immutable. If we were to add this, I'd expect it to be called "toSpliced".

That said, I think the API of splice is incredibly confusing, and it'd be more useful to look at what problems this is solving and how that can be best achieved, rather than cargoculting the worst Array method over to strings :-)

2 Likes

makes sense, i just thought it'd be neat addition since i have to do string splicing manually a lot. thank you for explaining it so nicely! : )

1 Like

Can you elaborate a bit more on your use cases for string splicing?

this would be really useful for normalizing phone number inputs:

let phone = "1234567890";
let formatted = phone.splice(3, 0, "-").splice(7, 0, "-");
console.log(formatted); // Output: "123-456-7890"

or dynamically inserting markup:

let text = "The quick brown fox";
let query = "quick";
let index = text.indexOf(query);
let highlighted = text.splice(index, 0, "<b>").splice(index + query.length + 3, 0, "</b>");
console.log(highlighted); // Output: "The <b>quick</b> brown fox"
1 Like

Given the complexity of the phone numbers people input (including symbols, area codes, country codes, etc), and of working with markup at all, I'm confused why you wouldn't prefer to use regular expressions and proper parsers rather than string manipulation?

I think that's just an example, but a replacement method could be indeed useful for several cases (I've thought about that during this Advent of Code, haha). For the sake of DX, I'd define a signature like this:

String.prototype.replaceSubstring = function(index, replacement, length = replacement.length) {
  return this.slice(0, index) + replacement + this.slice(index + length);
};

It'd be sweet to have other methods like .with (a special case of .replaceSubstring, in this case) or even .toReversed.

Edit: I've used replace as a "new" method - that would have been confusing, as there's already one - rather than a new signature for the method. For the sake of simplicity, I've renamed it with replaceSubstring.

1 Like

exactly! i thought of splice since, if implemented like Array.prototype.splice, it'd let you delete a substring without you having to pass in an empty string as the replacement string

1 Like

to be fair i've only discussed this with other engineers at work and the general consensus was that it would be convenient to have a method like this. let me run this idea past some community members online and hopefully i'll be able get back to you with some more thought out, diverse use cases.

1 Like

I'm not particularly fond of either of those use-case examples. The problem with both of them is that you're calling .splice() multiple times in a row, causing the index in the later calls to be effected by what got spliced in the earlier calls. One way to solve this would be to reverse the order of the .splice() so you splice the later text before the earlier one, but I think the most straight-forward solution would be to use the methods that are already currently available, like this:

Instead of:

let phone = "1234567890";
let formatted = phone.splice(3, 0, "-").splice(7, 0, "-");
console.log(formatted); // Output: "123-456-7890"

You could do this:

let phone = "1234567890";
let formatted = phone.slice(0, 3) + "-" + phone.slice(3, 6) + "-" + phone.slice(6);
console.log(formatted); // Output: "123-456-7890"

Notice how we can use 6 instead of 7, because we're not having to account for the fact that a "-" was already added earlier in the string, because the new string is being created in one go, instead of in steps.

Same thing with the markup example. Instead of this:

let text = "The quick brown fox";
let query = "quick";
let index = text.indexOf(query);
let highlighted = text.splice(index, 0, "<b>").splice(index + query.length + 3, 0, "</b>");
console.log(highlighted); // Output: "The <b>quick</b> brown fox"

We can have this (where you don't have to add an extra 3 to account for the 3 characters in <b>).

let text = "The quick brown fox";
let query = "quick";
let index = text.indexOf(query);
let highlighted = text.slice(0, index) + "<b>" + text.slice(index, index + query.length) + "</b>" + text.slice(index + query.length);
console.log(highlighted); // Output: "The <b>quick</b> brown fox"

Perhaps there are string helper methods we could add to improve this kind of use-case of inserting characters in the middle of the string, but I don't think .splice() does a particularly good job of it.

i see why string splice isn't already a thing! it seems existing methods already do a good enough job and .splice adds less convenience than new things to worry about.

1 Like