Reputation: 521
I'd like to split a string only the at the first n occurrences of a delimiter. I know, I could add them together using a loop, but isn't there a more straight forward approach?
var string = 'Split this, but not this';
var result = new Array('Split', 'this,', 'but not this');
Upvotes: 46
Views: 44227
Reputation: 45525
As per MDN:
string.split(separator, limit);
Update:
(Because using split
with limit
will not include the remaining part of the given string.)
var string = 'Split this, but not this',
arr = string.split(' '),
result = arr.slice(0,2);
result.push(arr.slice(2).join(' ')); // ["Split", "this,", "but not this"]
Update version 2 (one slice
shorter):
var string = 'Split this, but not this',
arr = string.split(' '),
result = arr.splice(0,2);
result.push(arr.join(' ')); // result is ["Split", "this,", "but not this"]
Upvotes: 50
Reputation: 81
This typescript version avoid to create a extra part in the end of array when the number of parts is greater than number of spplited parts.
const splitWithTail = (str: string, delim: string, count: number) => {
const parts = str.split(delim);
count = count < parts.length ? count : parts.length - 1;
const tail = parts.slice(count).join(delim);
const result = parts.slice(0, count);
result.push(tail);
return result;
}
splitWithTail("abc|def|ghi", "|", 9999);
// ["abc", "def", "ghi"]
Upvotes: 0
Reputation: 1048
For the specific case of splitting only the first occurrence, the code below can also be useful. In my tests it performed a lot better (about 40% in Chrome and 200% in Firefox) than the most voted answer.
https://jsbench.me/nckrqnwcry/1
function split(term = '', sep = ',') {
const split = term.split(sep, 1);
return [split[0], term.substr(split[0].length + sep.length)];
}
Upvotes: 1
Reputation: 3975
const splitAndAppend = (str, delim, count) => {
const arr = str.split(delim);
return [...arr.splice(0, count), arr.join(delim)];
}
Complexity O(n).
Upvotes: 2
Reputation: 104
My version, universal, supports RegExp and non-RegExp delimiters. Highly optimized. Tests provided. Why: since other RegExp versions are full of bugs and this is not a trivial function.
Usage:
"a b c d".split_with_tail(/ +/,3) = ['a','b','c d']
"a b c d".split_with_tail(' ',3) = ['a','b',' c d']
Code
String.prototype.split_with_tail = function(delimiter,limit)
{
if( typeof(limit) !== 'number' || limit < 1 ) return this.split(delimiter,limit);
var parts = this.split(delimiter,limit+1);
if( parts.length <= limit ) return parts;
parts.splice(-2,2);
limit = Math.floor(limit) - 1; // used later as index, speed optimization; limit can be float ..
if( delimiter instanceof RegExp ) {
// adds 'g' flag to any regexp:
delimiter += '';
var len = delimiter.lastIndexOf('/');
delimiter = new RegExp(delimiter.slice(1, len), delimiter.slice(len + 1)+'g');
len = 0;
while(limit--) len += parts[limit].length + (delimiter.exec(this))[0].length;
}
else {
var len = limit * (''+delimiter).length;
while(limit--) len += parts[limit].length;
}
parts.push(this.substring(len)); // adds tail, finally
return parts;
}
Tests
function test(str,delimiter,limit,result) {
if( JSON.stringify(result) !== JSON.stringify(str.split_with_tail(delimiter,limit)) ) {
console.log(arguments);
console.log(str.split_with_tail(delimiter,limit));
throw "lol";
}
}
test('',/ +/,undefined,['']);
test('',/ +/,3,['']);
test('a',/ +/,0.1,[]);
test('a',/ +/,1,['a']);
test('a a',/ +/,1,['a a']);
test('a a',/ +/,2.1,['a','a']);
test('a a a',/ +/,2.9,['a','a a']);
test('aaaaa aa a',/ +/,1,['aaaaa aa a']);
test('aaaaa aa a',/ +/,2,['aaaaa', 'aa a']);
test('a a',/ +/,2,['a','a']);
test('a',/ +/,3,['a']);
test('a a',/ +/,3,['a','a']);
test('a a a',/ +/,3,['a','a','a']);
test('a a a a',/ +/,3,['a','a','a a']);
test('a a a a',/ +/,4,['a','a','a','a']);
test('a aa aaa ',/ +/,4,['a','aa','aaa','']);
test('a a a a',/ +/,2,['a','a a a']);
test('a a a a',/ +/,1,['a a a a']);
test('a a a a',/ +/,0,[]);
test('a a a a',/ +/,undefined,['a','a','a','a']);
test('a a a a',/ +/,-1,['a','a','a','a']);
test('a',' ',3,['a']);
test('aaaaa aa a',' ',2,['aaaaa', 'aa a']);
test('aaaaa aa a',' ',2,['aaaaa','aa a']);
test('a a a',' ',3,['a','a','a']);
test('a a a a',' ',3,['a','a','a a']);
test('a a a a',' ',3,['a','a',' a a']);
test('a a a a',' ',2,['a','a a a']);
test('a a a a',' ',1,['a a a a']);
test('a a a a',' ',0,[]);
test('a a a a',' ',undefined,['a','a','','a','a']);
test('a a a a',' ',-1,['a','a','','a','a']);
test('1232425',2,3,['1','3','425']);
console.log("good!");
Upvotes: 0
Reputation: 169
var result = [string.split(' ',1).toString(), string.split(' ').slice(1).join(' ')];
Results in:
["Split", "this, but not this"]
Upvotes: 3
Reputation: 11576
Yet another implementation with limit;
// takes string input only
function split(input, separator, limit) {
input = input.split(separator);
if (limit) {
input = input.slice(0, limit - 1).concat(input.slice(limit - 1).join(separator));
}
return input;
}
Upvotes: 2
Reputation: 96
In my case I was trying to parse git grep stdout. So I had a {filename}:{linenumber}:{context}. I don't like splitting and then joining. We should be able to parse the string one time. You could simply step through each letter and split on the first two colons. A quicker way to do it out of the box is by using the match method and regex.
Hence,
txt.match(/(.+):(\d+):(.*)/)
Works great
Upvotes: 0
Reputation: 5606
Nothing a one simple regex can't do:
const string = 'Split this, but not this';
console.log(string.match(/^(\S+)\s*(\S+)?\s*([\s\S]+)?$/).slice(1));
Upvotes: 0
Reputation: 3164
Combination of split
and join
with ES6 features does this pretty neat:
let [str1, str2, ...str3] = string.split(' ');
str3 = str3.join(' ');
Upvotes: 26
Reputation: 3831
I like using shift
.
function splitFirstN(str,n,delim){
var parts = str.split(delim);
var r = [];
for(var i = 0; i < n; i++){
r.push(parts.shift());
}
r.push(parts.join(delim));
return r;
}
var str = 'Split this, but not this';
var result = splitFirstN(str,2,' ');
Upvotes: 0
Reputation: 282845
Yet another implementation I just wrote:
export function split(subject, separator, limit=undefined, pad=undefined) {
if(!limit) {
return subject.split(separator);
}
if(limit < 0) {
throw new Error(`limit must be non-negative`);
}
let result = [];
let fromIndex = 0;
for(let i=1; i<limit; ++i) {
let sepIdx = subject.indexOf(separator, fromIndex);
if(sepIdx < 0) {
break;
}
let substr = subject.slice(fromIndex, sepIdx);
result.push(substr);
fromIndex = sepIdx + separator.length;
}
result.push(subject.slice(fromIndex));
while(result.length < limit) {
result.push(pad);
}
return result;
}
Doesn't use regexes, nor does it over-split and re-join.
This version guarantees exactly limit
elements (will pad with undefined
s if there aren't enough separators); this makes it safe to do this kind of ES6 stuff:
let [a,b,c] = split('a$b','$',3,null);
// a = 'a', b = 'b', c = null
Upvotes: 0
Reputation: 1575
Improved version of a sane limit
implementation with proper RegEx support:
function splitWithTail(value, separator, limit) {
var pattern, startIndex, m, parts = [];
if(!limit) {
return value.split(separator);
}
if(separator instanceof RegExp) {
pattern = new RegExp(separator.source, 'g' + (separator.ignoreCase ? 'i' : '') + (separator.multiline ? 'm' : ''));
} else {
pattern = new RegExp(separator.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1'), 'g');
}
do {
startIndex = pattern.lastIndex;
if(m = pattern.exec(value)) {
parts.push(value.substr(startIndex, m.index - startIndex));
}
} while(m && parts.length < limit - 1);
parts.push(value.substr(pattern.lastIndex));
return parts;
}
Usage example:
splitWithTail("foo, bar, baz", /,\s+/, 2); // -> ["foo", "bar, baz"]
Built for & tested in Chrome, Firefox, Safari, IE8+.
Upvotes: 1
Reputation: 4952
Hi there i had the same problem wanted to split only several times, couldnt find anything so i just extended the DOM - just a quick and dirty solution but it works :)
String.prototype.split = function(seperator,limit) {
var value = "";
var hops = [];
// Validate limit
limit = typeof(limit)==='number'?limit:0;
// Join back given value
for ( var i = 0; i < this.length; i++ ) { value += this[i]; }
// Walkthrough given hops
for ( var i = 0; i < limit; i++ ) {
var pos = value.indexOf(seperator);
if ( pos != -1 ) {
hops.push(value.slice(0,pos));
value = value.slice(pos + seperator.length,value.length)
// Done here break dat
} else {
break;
}
}
// Add non processed rest and return
hops.push(value)
return hops;
}
In your case would look like that
>>> "Split this, but not this".split(' ',2)
["Split", "this,", "but not this"]
Upvotes: 2
Reputation: 4941
For this you could use Split(delimiter) and choose a delimiter.
var testSplit = "Split this, but not this";
var testParts= testSplit.Split(",");
var firstPart = testParts[1];
// firstPart = "Split this"
Not 100% on my syntax I havent used javascript in quite some time. But I know this is how its done...
EDIT** Sorry, my mistake. Now I believe I know what your asking and I think the easiest way to do this is using substr. Very easy, no loops required. Just made an example, works perfect
// so first, we want to get everything from 0 - the first occurence of the comma.
// next, we want to get everything after the first occurence of the comma. (if you only define one parameter, substr will take everything after that parameter.
var testString = "Split this, but this part, and this part are one string";
var part1 = testString.substr(0,testString.indexOf(','));
var part2 = testString.substr(testString.indexOf(','));
//part1 = "Split this"
//part2= "but this part, and this part are one string"
Upvotes: 4
Reputation: 6068
Using Array.slice:
function splitWithTail(str,delim,count){
var parts = str.split(delim);
var tail = parts.slice(count).join(delim);
var result = parts.slice(0,count);
result.push(tail);
return result;
}
Results:
splitWithTail(string," ",2)
// => ["Split", "this,", "but not this"]
Upvotes: 25
Reputation: 3958
var s='Split this, but not this', a=s.split(','), b=a[0].split(' ');
b.push(a[1]);
alert(b);
alerts ['Split', 'this', 'but not this']
Upvotes: 0
Reputation: 1074188
Although you can give split
a limit, you won't get back what you've said you want. Unfortunately, you will have to roll your own on this, e.g.:
var string = 'Split this, but not this';
var result = string.split(' ');
if (result.length > 3) {
result[2] = result.slice(2).join(' ');
result.length = 3;
}
But even then, you end up modifying the number of spaces in the latter parts of it. So I'd probably just do it the old-fashioned write-your-own-loop way:
function splitWithLimit(str, delim, limit) {
var index,
lastIndex = 0,
rv = [];
while (--limit && (index = str.indexOf(delim, lastIndex)) >= 0) {
rv.push(str.substring(lastIndex, index));
lastIndex = index + delim.length;
}
if (lastIndex < str.length) {
rv.push(str.substring(lastIndex));
}
return rv;
}
Upvotes: 2
Reputation: 413720
The JavaScript ".split()" function already accepts a second parameter giving the maximum number of splits to perform. However, it doesn't retain the tail end of your original string; you'd have to glue it back on.
Another approach would be to iteratively shave off a leading portion of the string with a regex, stopping when you've gotten your limit.
var str = "hello out there cruel world";
var parts = [];
while (parts.length < 3) { // "3" is just an example
str = str.replace(/^(\w+)\s*(.*)$/, function(_, word, remainder) {
parts.push(word);
return remainder;
});
}
parts.push(str);
edit — and it just occurs to me that another simple way would be to just use plain ".split()", pluck off the first few parts, and then just ".slice()" and ".join()" the rest.
Upvotes: 9