'Split string with limit, where last string contains the remainder

e.g. if I run this javascript:

var str = 'hello_world_there';
var parts = str.split('_', 2);

var p1 = parts[0];
var p2 = parts[1];

at the end, p1 contains "hello", and p2 contains "world".

I'd like p1 to contain "hello", and p2 to contain "world_there". i.e. I want p2 to contain the rest of the string, regardless of how many underscores it has (similar to how c#'s String.Split(char[] separator, int count) behaves.

Any workarounds ?



Solution 1:[1]

This is slightly different from taking the rest as is, but this was useful in my case:

const [a, ...b] = 'foo/bar/baz'.split('/');
// => a: 'foo', b: ['bar', 'baz']

Solution 2:[2]

Write your own function:

function split(string, delimiter, n) {
    var parts = string.split(delimiter);
    return parts.slice(0, n - 1).concat([parts.slice(n - 1).join(delimiter)]);
}

Solution 3:[3]

Just try with:

var parts = str.split('_', 2);
var p1 = parts.shift();
var p2 = parts.join('_');

or:

var index = str.indexOf('_');
var p1 = str.substr(0, index);
var p2 = str.substr(index + 1);

Solution 4:[4]

For your specific case where you just want two parts, the simplest thing is indexOf and substring:

const index = text.indexOf("_");
const p0 = index === -1 ? text : text.substring(0, index);
const p1 = index === -1 ? "" : text.substring(index + 1);

Live Example:

const text = "hello_world_there";
const index = text.indexOf("_");
const p0 = index === -1 ? text : text.substring(0, index);
const p1 = index === -1 ? "" : text.substring(index + 1);
console.log(`p0 = "${p0}", p1 = "${p1}"`);

I've found I want that often enough I have it in a utility module:

const twoParts = (str, delim = " ") => {
    const index = str.indexOf("_");
    if (index === -1) {
        return [str, ""];
    }
    return [
        str.substring(0, index),
        str.substring(index + delim.length),
    ];
};

But if you wanted to do more than just teh two parts, an approach that doesn't require rejoining the string after the fact would be to use a regular expression with capture groups:

const result = text.match(/^([^_]+)_+([^_]+)_+(.*)$/);
const [_, p0 = "", p1 = "", p2 = ""] = result ?? [];

Live Example:

const text = "hello_world_xyz_there";
const result = text.match(/^([^_]+)_+([^_]+)_+(.*)$/);
const [_, p0 = "", p1 = "", p2 = ""] = result ?? [];
console.log(`p0 = "${p0}", p1 = "${p1}", p2 = "${p2}"`);

Solution 5:[5]

Regexp solution (you can change "|" to other chars e.g. "__" but not to "_")

var [p1,p2] = str.replace(/_/,"|").split("|");

let str = 'hello_world_there'
let [p1,p2] = str.replace(/_/,"|").split("|");

console.log("p1:",p1);
console.log("p2:",p2);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 amiq11
Solution 2 Blender
Solution 3 hsz
Solution 4 T.J. Crowder
Solution 5 Kamil Kiełczewski