24

I would like to split a string into fixed-length (N, for example) pieces. Of course, last piece could be shorter, if original string's length is not multiple of N.

I need the fastest method to do it, but also the simplest to write. The way I have been doing it until now is the following:

var a = 'aaaabbbbccccee';
var b = [];
for(var i = 4; i < a.length; i += 4){ // length 4, for example
    b.push(a.slice(i-4, i));
}
b.push(a.slice(a.length - (4 - a.length % 4))); // last fragment

I think there must be a better way to do what I want. But I don't want extra modules or libraries, just simple JavaScript if it's possible.

Before ask, I have seen some solutions to resolve this problem using other languages, but they are not designed with JavaScript in mind.

sgmonda
  • 2,615
  • 1
  • 19
  • 29
  • Do you really write all that out everytime just to avoid a function call? – Esailija May 06 '12 at 23:04
  • What makes you think that you can have fastest code execution _and_ simplicity of implementation? Those two qualities are often competing. – Matt Ball May 06 '12 at 23:05
  • 1
    Here is another implementation, but still very similar to yours: http://phpjs.org/functions/str_split:530 – Niko May 06 '12 at 23:05
  • @Esailija He said that somewhere? @sgmonda I don't think there is much better way. As javascript `string` does not have a method to do this, few lines of code like you have are fine. And this should be fast enough too. – Imp May 06 '12 at 23:07
  • Try using `String.substring` or `String.substr` instead of `String.slice` and comparing performance. – Matt Ball May 06 '12 at 23:08
  • 1
    @Imp I don't know any other explanation for it needing to have a requirement to be easy to write other than writing it out every time. It wouldn't matter if it's very optimized but ugly code behind a function you just call. – Esailija May 06 '12 at 23:09
  • I took the liberty to create a jsPerf test case with the proposed solutions. Your method is the fastest in Chrome and Firefox(at least) (about 50% faster than the next method). So always test and compare code. Shorter code is not automatically faster. http://jsperf.com/chunk-methods – Felix Kling May 07 '12 at 08:15
  • Thank you, @FelixKling. Depending on the situation, we'll have to decide between efficiency or simplicity. – sgmonda May 07 '12 at 10:29

3 Answers3

47

You can try this:

var a = 'aaaabbbbccccee';
var b = a.match(/(.{1,4})/g);
Danilo Valente
  • 11,270
  • 8
  • 53
  • 67
9

See this related question: https://stackoverflow.com/a/10456644/711085 and https://stackoverflow.com/a/8495740/711085 (See performance test in comments if performance is an issue.)

First (slower) link:

[].concat.apply([],
    a.split('').map(function(x,i){ return i%4 ? [] : a.slice(i,i+4) })
)

As a string prototype:

String.prototype.chunk = function(size) {
    return [].concat.apply([],
        this.split('').map(function(x,i){ return i%size ? [] : this.slice(i,i+size) }, this)
    )
}

Demo:

> '123412341234123412'.chunk(4)
["1234", "1234", "1234", "1234", "12"]
Community
  • 1
  • 1
ninjagecko
  • 88,546
  • 24
  • 137
  • 145
  • Now that the regex answer does not require writing `/(.{32}|.{31}|.{30}............................|.{2}|.)/g`, I would have to recommend that answer since it very concise. =) This solution is better only for chunking arrays. – ninjagecko May 06 '12 at 23:42
  • This seems perfect for the same problem but applied to arrays. Thank you! For spliting strings I prefer the above solution. – sgmonda May 07 '12 at 00:12
  • @FelixKling: Ah interesting. Though one should not take the test to imply (probably incorrectly) that Array.concat is slow. The most likely culprit is 1) the conversion to an array (testable by doing `.split('')` in preparation code), and 2) performing an unnecessary 4x if statements. Nevertheless the for-loop version is definitely going to be more efficient; I was undecided which of the two answers I should link. Thanks, I'll edit to point this out. – ninjagecko May 07 '12 at 09:43
1
function stringToChanks(string, chunkSize) {
    const chunks = [];
    while (string.length > 0) {
        chunks.push(string.substring(0, chunkSize));
        string = string.substring(chunkSize, string.length);
    }
    return chunks
}
itzhar
  • 12,743
  • 6
  • 56
  • 63