Javascript and regex: split string and keep the separator

Viewed 131218

I have a string:

var string = "aaaaaa<br />&dagger; bbbb<br />&Dagger; cccc"

And I would like to split this string with the delimiter <br /> followed by a special character.

To do that, I am using this:

string.split(/<br \/>&#?[a-zA-Z0-9]+;/g);

I am getting what I need, except that I am losing the delimiter. Here is the example: http://jsfiddle.net/JwrZ6/1/

How can I keep the delimiter?

11 Answers

I made a modification to jichi's answer, and put it in a function which also supports multiple letters.

String.prototype.splitAndKeep = function(separator, method='seperate'){
    var str = this;
    if(method == 'seperate'){
        str = str.split(new RegExp(`(${separator})`, 'g'));
    }else if(method == 'infront'){
        str = str.split(new RegExp(`(?=${separator})`, 'g'));
    }else if(method == 'behind'){
        str = str.split(new RegExp(`(.*?${separator})`, 'g'));
        str = str.filter(function(el){return el !== "";});
    }
    return str;
};

jichi's answers 3rd method would not work in this function, so I took the 4th method, and removed the empty spaces to get the same result.

edit: second method which excepts an array to split char1 or char2

String.prototype.splitAndKeep = function(separator, method='seperate'){
    var str = this;
    function splitAndKeep(str, separator, method='seperate'){
        if(method == 'seperate'){
            str = str.split(new RegExp(`(${separator})`, 'g'));
        }else if(method == 'infront'){
            str = str.split(new RegExp(`(?=${separator})`, 'g'));
        }else if(method == 'behind'){
            str = str.split(new RegExp(`(.*?${separator})`, 'g'));
            str = str.filter(function(el){return el !== "";});
        }
        return str;
    }
    if(Array.isArray(separator)){
        var parts = splitAndKeep(str, separator[0], method);
        for(var i = 1; i < separator.length; i++){
            var partsTemp = parts;
            parts = [];
            for(var p = 0; p < partsTemp.length; p++){
                parts = parts.concat(splitAndKeep(partsTemp[p], separator[i], method));
            }
        }
        return parts;
    }else{
        return splitAndKeep(str, separator, method);
    }
};

usage:

str = "first1-second2-third3-last";

str.splitAndKeep(["1", "2", "3"]) == ["first", "1", "-second", "2", "-third", "3", "-last"];

str.splitAndKeep("-") == ["first1", "-", "second2", "-", "third3", "-", "last"];

If you group the split pattern, its match will be kept in the output and it is by design:

If separator is a regular expression with capturing parentheses, then each time separator matches, the results (including any undefined results) of the capturing parentheses are spliced into the output array.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split#description

You don't need a lookahead or global flag unless your search pattern uses one.

const str = `How much wood would a woodchuck chuck, if a woodchuck could chuck wood?`

const result = str.split(/(\s+)/);
console.log(result);

// We can verify the result
const isSame = result.join('') === str;
console.log({ isSame });

You can use multiple groups. You can be as creative as you like and what remains outside the groups will be removed:

const str = `How much wood would a woodchuck chuck, if a woodchuck could chuck wood?`

const result = str.split(/(\s+)(\w{1,2})\w+/);
console.log(result, result.join(''));

Most of the existing answers predate the introduction of lookbehind assertions in JavaScript in 2018. You didn't specify how you wanted the delimiters to be included in the result. One typical use case would be sentences delimited by punctuation ([.?!]), where one would want the delimiters to be included at the ends of the resulting strings. This corresponds to the fourth case in the accepted answer, but as noted there, that solution only works for single characters. Arbitrary strings with the delimiters appended at the end can be formed with a lookbehind assertion:

'It is. Is it? It is!'.split(/(?<=[.?!])/)
/* [ 'It is.', ' Is it?', ' It is!' ] */

I know that this is a bit late but you could also use lookarounds

var string = "aaaaaa<br />&dagger; bbbb<br />&Dagger; cccc";
var array = string.split(/(?<=<br \/>)/);
console.log(array);

I've also came up with this solution. No regex needed, very readable.

const str = "hello world what a great day today balbla"
const separatorIndex = str.indexOf("great")
const parsedString = str.slice(separatorIndex)

console.log(parsedString)

or in one line:

const str = "hello what a great day today balbla"
const parsedString = str.indexOf(str.indexOf("great"))
console.log(parsedString)

Related