305

As the title says, I've got a string and I want to split into segments of n characters.

For example:

var str = 'abcdefghijkl';

after some magic with n=3, it will become

var arr = ['abc','def','ghi','jkl'];

Is there a way to do this?

Ivar
  • 6,138
  • 12
  • 49
  • 61
Ben
  • 54,723
  • 49
  • 178
  • 224

18 Answers18

536

var str = 'abcdefghijkl';
console.log(str.match(/.{1,3}/g));

Note: Use {1,3} instead of just {3} to include the remainder for string lengths that aren't a multiple of 3, e.g:

console.log("abcd".match(/.{1,3}/g)); // ["abc", "d"]

A couple more subtleties:

  1. If your string may contain newlines (which you want to count as a character rather than splitting the string), then the . won't capture those. Use /[\s\S]{1,3}/ instead. (Thanks @Mike).
  2. If your string is empty, then match() will return null when you may be expecting an empty array. Protect against this by appending || [].

So you may end up with:

var str = 'abcdef \t\r\nghijkl';
var parts = str.match(/[\s\S]{1,3}/g) || [];
console.log(parts);

console.log(''.match(/[\s\S]{1,3}/g) || []);
Ruslan López
  • 4,433
  • 2
  • 26
  • 37
David Tang
  • 92,262
  • 30
  • 167
  • 149
  • This is technically the better answer as it will grab all the text from a string that's not evenly divisible by 3 (it will grab the last 2 or 1 characters). – Erik Jun 07 '11 at 00:36
  • 8
    Use `[\s\S]` instead of `.` so as not to fail on newlines. – Mike Samuel Jun 07 '11 at 00:42
  • 2
    You may want to start a new cycle on every line. If you really do have newlines, they probably indicate some type of transition. str.match(/.{1,3}/gm) may be a better choice. – kennebec Jun 07 '11 at 02:51
  • +1 **Careful:** `''.match(/.{1,3}/g)` and `''.match(/.{3}/g)` return `null` instead of an empty array. – Web_Designer Jun 18 '14 at 16:15
  • 4
    Is it possible to have a variable in the place of number 3? – Ana Claudia Aug 19 '14 at 10:16
  • Amazing approach! In a similar fashion, `str.match(/.{1}/g)` will return each separate character of the string (pretty much like str.split('') would). Thanks! – Vidal Quevedo Nov 25 '14 at 20:25
  • See one of the comments further down: ```str.match(new RegExp('.{1,' + size + '}', 'g'));``` – Patrick Chu May 23 '18 at 10:47
  • You would have to use `new RegExp(regex,flags)` instead. Here's a quick little function I made to create a RegExp for a specified length and cache it to a bound object. `let splitter=(function(n){let prop='cache'+(n|0);if (!this[prop]) {this[prop]=new RegExp(`.{1,${n}}`,'g');}return this[prop];}).bind({});'1234567890'.match(splitter(3)); //["123", "456", "789", "0"]` – Chinoto Vokro Oct 17 '18 at 18:44
74

If you didn't want to use a regular expression...

var chunks = [];

for (var i = 0, charsLength = str.length; i < charsLength; i += 3) {
    chunks.push(str.substring(i, i + 3));
}

jsFiddle.

...otherwise the regex solution is pretty good :)

alex
  • 479,566
  • 201
  • 878
  • 984
  • 1
    +1 cos I'd prefer this if the `3` is variable as suggested by the OP. It's more readable than concatenating a regexp string. – David Tang Jun 07 '11 at 00:56
  • 1
    if only you could wrap that into a useful function ready to be used – mjs Sep 28 '16 at 09:49
  • 1
    This is more than 10x faster than the regex option, so I would go with this (inside of a function) http://jsbench.github.io/#9cb819bf1ce429575f8535a211f72d5a – Job Mar 27 '17 at 08:33
  • 1
    My previous statement applies to Chromium (also, I was too late with editing the previous comment hence the new one). On Firefox it's currently "only" 30% faster on my machine, but that's still consistently better. – Job Mar 27 '17 at 08:50
  • is this sustainable over huge lengths of string? – J-Cake Dec 19 '19 at 12:49
44
str.match(/.{3}/g); // => ['abc', 'def', 'ghi', 'jkl']
maerics
  • 151,642
  • 46
  • 269
  • 291
  • This works for `3` for me but returns `null` with `250`. – Jim Apr 29 '20 at 03:14
  • Try `"AB".match(/.{3}/g)` and you'll see why this won't work. It needs at least {3} characters to complete a match. `"ABCAB".match(/.{3}/g)` will also return only the complete match `['ABC']` – caulitomaz Sep 28 '22 at 07:53
19

Building on the previous answers to this question; the following function will split a string (str) n-number (size) of characters.

function chunk(str, size) {
    return str.match(new RegExp('.{1,' + size + '}', 'g'));
}

Demo

(function() {
  function chunk(str, size) {
    return str.match(new RegExp('.{1,' + size + '}', 'g'));
  }
  
  var str = 'HELLO WORLD';
  println('Simple binary representation:');
  println(chunk(textToBin(str), 8).join('\n'));
  println('\nNow for something crazy:');
  println(chunk(textToHex(str, 4), 8).map(function(h) { return '0x' + h }).join('  '));
  
  // Utiliy functions, you can ignore these.
  function textToBin(text) { return textToBase(text, 2, 8); }
  function textToHex(t, w) { return pad(textToBase(t,16,2), roundUp(t.length, w)*2, '00'); }
  function pad(val, len, chr) { return (repeat(chr, len) + val).slice(-len); }
  function print(text) { document.getElementById('out').innerHTML += (text || ''); }
  function println(text) { print((text || '') + '\n'); }
  function repeat(chr, n) { return new Array(n + 1).join(chr); }
  function textToBase(text, radix, n) {
    return text.split('').reduce(function(result, chr) {
      return result + pad(chr.charCodeAt(0).toString(radix), n, '0');
    }, '');
  }
  function roundUp(numToRound, multiple) { 
    if (multiple === 0) return numToRound;
    var remainder = numToRound % multiple;
    return remainder === 0 ? numToRound : numToRound + multiple - remainder;
  }
}());
#out {
  white-space: pre;
  font-size: 0.8em;
}
<div id="out"></div>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
9

If you really need to stick to .split and/or .raplace, then use /(?<=^(?:.{3})+)(?!$)/g

For .split:

var arr = str.split( /(?<=^(?:.{3})+)(?!$)/ )
// [ 'abc', 'def', 'ghi', 'jkl' ]

For .replace:

var replaced = str.replace( /(?<=^(?:.{3})+)(?!$)/g, ' || ' )
// 'abc || def || ghi || jkl'



/(?!$)/ is to not stop at end of the string. Without it's:

var arr = str.split( /(?<=^(?:.{3})+)/ )
// [ 'abc', 'def', 'ghi', 'jkl' ] // is fine
var replaced = str.replace( /(?<=^(.{3})+)/g, ' || ')
// 'abc || def || ghi || jkl || ' // not fine

Ignoring group /(?:...)/ is to prevent duplicating entries in the array. Without it's:

var arr = str.split( /(?<=^(.{3})+)(?!$)/ )
// [ 'abc', 'abc', 'def', 'abc', 'ghi', 'abc', 'jkl' ] // not fine
var replaced = str.replace( /(?<=^(.{3})+)(?!$)/g, ' || ' )
// 'abc || def || ghi || jkl' // is fine
denisde4ev
  • 91
  • 1
  • 4
4

My solution (ES6 syntax):

const source = "8d7f66a9273fc766cd66d1d";
const target = [];
for (
    const array = Array.from(source);
    array.length;
    target.push(array.splice(0,2).join(''), 2));

We could even create a function with this:

function splitStringBySegmentLength(source, segmentLength) {
    if (!segmentLength || segmentLength < 1) throw Error('Segment length must be defined and greater than/equal to 1');
    const target = [];
    for (
        const array = Array.from(source);
        array.length;
        target.push(array.splice(0,segmentLength).join('')));
    return target;
}

Then you can call the function easily in a reusable manner:

const source = "8d7f66a9273fc766cd66d1d";
const target = splitStringBySegmentLength(source, 2);

Cheers

Jesus Gonzalez
  • 411
  • 6
  • 17
4
var str = 'abcdefghijkl';
var res = str.match(/.../g)
console.log(res)

here number of dots determines how many text you want in each word.

aakash4dev
  • 774
  • 4
  • 13
3
const chunkStr = (str, n, acc) => {     
    if (str.length === 0) {
        return acc
    } else {
        acc.push(str.substring(0, n));
        return chunkStr(str.substring(n), n, acc);
    }
}
const str = 'abcdefghijkl';
const splittedString = chunkStr(str, 3, []);

Clean solution without REGEX

seriouspat
  • 31
  • 1
3

My favorite answer is gouder hicham's. But I revised it a little so that it makes more sense to me.

let myString = "Able was I ere I saw elba";

let splitString = [];
for (let i = 0; i < myString.length; i = i + 3) {
    splitString.push(myString.slice(i, i + 3));
}

console.log(splitString);

Here is a functionalized version of the code.


function stringSplitter(myString, chunkSize) {
    let splitString = [];
    for (let i = 0; i < myString.length; i = i + chunkSize) {
        splitString.push(myString.slice(i, i + chunkSize));
    }
    return splitString;
}

And the function's use:

let myString = "Able was I ere I saw elba";
let mySplitString = stringSplitter(myString, 3);
console.log(mySplitString);

And it's result:

>(9) ['Abl', 'e w', 'as ', 'I e', 're ', 'I s', 'aw ', 'elb', 'a']
garydavenport73
  • 379
  • 2
  • 8
2

try this simple code and it will work like magic !

let letters = "abcabcabcabcabc";
// we defined our variable or the name whatever
let a = -3;
let finalArray = [];
for (let i = 0; i <= letters.length; i += 3) {
    finalArray.push(letters.slice(a, i));
  a += 3;
}
// we did the shift method cause the first element in the array will be just a string "" so we removed it
finalArray.shift();
// here the final result
console.log(finalArray);
camille
  • 16,432
  • 18
  • 38
  • 60
gouder hicham
  • 123
  • 10
1
function chunk(er){
return er.match(/.{1,75}/g).join('\n');
}

Above function is what I use for Base64 chunking. It will create a line break ever 75 characters.

Dave Brown
  • 923
  • 9
  • 6
1

Here we intersperse a string with another string every n characters:

export const intersperseString = (n: number, intersperseWith: string, str: string): string => {

  let ret = str.slice(0,n), remaining = str;

  while (remaining) {
    let v = remaining.slice(0, n);
    remaining = remaining.slice(v.length);
    ret += intersperseWith + v;
  }

  return ret;

};

if we use the above like so:

console.log(splitString(3,'|', 'aagaegeage'));

we get:

aag|aag|aeg|eag|e

and here we do the same, but push to an array:

export const sperseString = (n: number, str: string): Array<string> => {

  let ret = [], remaining = str;

  while (remaining) {
    let v = remaining.slice(0, n);
    remaining = remaining.slice(v.length);
    ret.push(v);
  }

  return ret;

};

and then run it:

console.log(sperseString(5, 'foobarbaztruck'));

we get:

[ 'fooba', 'rbazt', 'ruck' ]

if someone knows of a way to simplify the above code, lmk, but it should work fine for strings.

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • your first snippet wasnt working as expected. I modified here: https://jsfiddle.net/omarojo/ksvx2txb/261/ – omarojo Sep 13 '18 at 04:33
1

Here's a way to do it without regular expressions or explicit loops, although it's stretching the definition of a one liner a bit:

const input = 'abcdefghijlkm';
    
// Change `3` to the desired split length.
const output = input.split('').reduce((s, c) => {
    let l = s.length-1; 
    (s[l] && s[l].length < 3) ? s[l] += c : s.push(c); 
    return s;
}, []);

console.log(output);  // output: [ 'abc', 'def', 'ghi', 'jlk', 'm' ]

It works by splitting the string into an array of individual characters, then using Array.reduce to iterate over each character. Normally reduce would return a single value, but in this case the single value happens to be an array, and as we pass over each character we append it to the last item in that array. Once the last item in the array reaches the target length, we append a new array item.

Sergey Kaunov
  • 140
  • 1
  • 8
Malvineous
  • 25,144
  • 16
  • 116
  • 151
1

Coming a little later to the discussion but here a variation that's a little faster than the substring + array push one.

// substring + array push + end precalc
var chunks = [];

for (var i = 0, e = 3, charsLength = str.length; i < charsLength; i += 3, e += 3) {
    chunks.push(str.substring(i, e));
}

Pre-calculating the end value as part of the for loop is faster than doing the inline math inside substring. I've tested it in both Firefox and Chrome and they both show speedup.

You can try it here

Dragonaire
  • 313
  • 3
  • 7
0

Some clean solution without using regular expressions:

/**
* Create array with maximum chunk length = maxPartSize
* It work safe also for shorter strings than part size
**/
function convertStringToArray(str, maxPartSize){

  const chunkArr = [];
  let leftStr = str;
  do {

    chunkArr.push(leftStr.substring(0, maxPartSize));
    leftStr = leftStr.substring(maxPartSize, leftStr.length);

  } while (leftStr.length > 0);

  return chunkArr;
};

Usage example - https://jsfiddle.net/maciejsikora/b6xppj4q/.

I also tried to compare my solution to regexp one which was chosen as right answer. Some test can be found on jsfiddle - https://jsfiddle.net/maciejsikora/2envahrk/. Tests are showing that both methods have similar performance, maybe on first look regexp solution is little bit faster, but judge it Yourself.

Maciej Sikora
  • 19,374
  • 4
  • 49
  • 50
0
function str_split(string, length = 1) {
    if (0 >= length)
        length = 1;
    
    if (length == 1)
        return string.split('');

    var string_size = string.length;
    var result = [];

    for (let i = 0; i < string_size / length; i++)
        result[i] = string.substr(i * length, length);

    return result;
}

str_split(str, 3)

Results (Chrome 104)

0

I was looking for a solution to convert a number like 1000000 to 1,000,000 and I definitely don't want to keep running heavy Regex operations, slowing down my code.

Alex's solution was perfect (among all the regex based solutions!). I've modified and wrapped that up into a function that can format a number into thousand-separated number:

function thouSep(str, delim=","){

    var chunks = [];
    str+=""

    for (var i = str.length; i >0; i -= 3) {
        chunks.push(str.substring(i-3, i));
    }
    
    f = chunks.reverse().join(delim)
    return f
}

Usage:

thouSep(100000) => "100,000"
thouSep(10000)  =>  "10,000"
thouSep(10)     =>  "10"

thouSep(1000000)  =>  "1,000,000"

thouSep("Fabulous", "-") => "Fa-bul-ous"

Possible improvements:

  1. Account for decimals: 12345.6789 => 12,345.6789
  2. Ability to vary length of segments, instead of just 3s.
a20
  • 5,495
  • 2
  • 30
  • 27
-1
var b1 = ""; 

function myFunction(n) { 
  if(str.length>=3){
    var a = str.substring(0,n);
    b1 += a+ "\n"
    str = str.substring(n,str.length)
    myFunction(n) 
  } 
  else { 
    if(str.length>0){
      b1 += str 
    }
    
    console.log(b1)
  } 
}
      
myFunction(4)
Ben
  • 54,723
  • 49
  • 178
  • 224