Ben
Ben

Reputation: 57209

How can I split a string into segments of n characters?

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?

Upvotes: 323

Views: 307776

Answers (21)

Natry Clorum
Natry Clorum

Reputation: 1

This questions even in 2025 seems to be very interesting challenge!

My solution with ES6 syntax:

let str = 'abcdefghijklm';

let chs = [...str];
let arr = [];

let a, b, c;
while (chs.length) {
    ([a, b, c, ...chs] = chs);
    
    let abc = [a, b, c].join('');

    arr.push(abc);
}

console.log('arr', arr);

Upvotes: 0

cruciformhawk7
cruciformhawk7

Reputation: 53

Here is another solution to this using reduce:

function chunkString(sStr, iLen) {
   return [...sStr].reduce((aChunks, sChar, iIdx) => ( 
    aChunks.push(iIdx % iLen === 0 ? sChar : aChunks.pop() + sChar), aChunks), [])
}

This works by splitting the string into an array of characters, and pushing a new entry to the list when the index points to a number that's a remainder of iLen, and when not, puts the character at the end of the last entry in the array.

Example usage:

// Proper groups
chunkString('abcdefghijkl', 3) // => ['abc', 'def', 'ghi', 'jkl' ]

// Improper groups
chunkString('abcdefghijklmn', 3) // => [ 'abc', 'def', 'ghi', 'jkl', 'mn' ]

// Get rid of improper groups using a filter:
chunkString('abcdefghijklmn', 3)
  .filter(entry => entry.length === 3) // => [ 'abc', 'def', 'ghi', 'jkl' ]

Upvotes: 0

Alexander Mills
Alexander Mills

Reputation: 99960

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(intersperseString(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.

Upvotes: 1

alex
alex

Reputation: 490143

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

var str = 'abcdefghijkl';

var chunks = [];

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

console.log(chunks);

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

Upvotes: 76

Constant Addo
Constant Addo

Reputation: 1

function groupChars(str = "ABCDEFGHI",inGroupsOf = 3){
    let groups = [];
    for (let i = 0; i < str.length; i++) {
        (i%inGroupsOf) == 0 ? groups.push(str.slice(i,inGroupsOf + i)):null     
    }
    return groups;
}

console.log(groupChars("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));

The function groupChars takes two arguments - str and inGroupsOf. The inGroupsOf parameter determines the number of characters each group is to be made of.

  1. We create a groups array - let groups = [];
  2. We loop through the string using a for loop
  3. We check if the index of each string modulus inGroupsOf argument is zero: ( i % inGroupsOf) == 0;
  4. If true, we push in the value into the groups array
  5. We return the groups array from the function

Upvotes: 0

a20
a20

Reputation: 5641

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.

Upvotes: 0

NITISH KUMAR
NITISH KUMAR

Reputation: 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)

Upvotes: -1

aakash4dev
aakash4dev

Reputation: 1176

var str = 'abcdefghijkl';
var res = str.match(/.../g)
console.log(res)

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

Upvotes: 7

Malvineous
Malvineous

Reputation: 27300

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.

Upvotes: 2

user19794193
user19794193

Reputation:

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)

Upvotes: 0

garydavenport73
garydavenport73

Reputation: 479

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']

Upvotes: 4

denisde4ev
denisde4ev

Reputation: 91

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

Upvotes: 9

gouder hicham
gouder hicham

Reputation: 133

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);

Upvotes: 2

Dragonaire
Dragonaire

Reputation: 313

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

Upvotes: 1

seriouspat
seriouspat

Reputation: 31

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

Upvotes: 3

Jesus Gonzalez
Jesus Gonzalez

Reputation: 411

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

Upvotes: 4

Maciej Sikora
Maciej Sikora

Reputation: 20132

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.

Upvotes: 0

David Tang
David Tang

Reputation: 93664

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) || []);

Upvotes: 565

Dave Brown
Dave Brown

Reputation: 939

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.

Upvotes: 1

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48600

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>

Upvotes: 20

maerics
maerics

Reputation: 156374

str.match(/.{3}/g); // => ['abc', 'def', 'ghi', 'jkl']

Upvotes: 45

Related Questions