Chunking an Array with ColdFusion Redux

Revisiting chunking ColdFusion arrays in 2025 cfml

July 17, 2025 / Robert Zehnder

Still talking about chunking arrays? Yep.

It’s been four years since my original post, and while it’s not something I deal with daily, chunking comes in handy — especially when working with large payloads that need to be split for APIs or batch processing.

Over the years, I’ve experimented with several versions of this logic. Not all code is created equal — some are more readable, others more performant. What works great on small arrays can crawl on larger ones. So, I decided to revisit this, benchmark a few versions, and share the results.

For testing, I generated an array of 100,000 elements using randomized data:

source = [];
for (i = 1; i <= 100000; i++) {
  source[i] = randRange(1, 9999, "SHA1PRNG");
}

Version 1: Simple & Clean (but slow)

Readable? Yes. Fast? Not really. This version averaged 1300ms on BoxLang and Adobe 2025 — making it the slowest.

function chunkArrayV1(input, chunkSize) {
  var output[1] = [];
  var currentChunk = 1;

  input.each((item, index) => {
    output[currentChunk].append(item);
    if (index % chunkSize == 0 && index < input.len()) {
      output[++currentChunk] = [];
    }
  });

  return output;
}

Version 2: Compact & Quick (but less readable)

Surprisingly performant — especially on newer engines like BoxLang and Lucee 6. Less so on Lucee 5.

function chunkArrayV2(input, chunkSize) {
  var out = [];
  for (var i = 1; i <= ceiling(input.len() / chunkSize); i++) {
    out.append(
      i == ceiling(input.len() / chunkSize)
        ? input.slice(1 + (i - 1) * chunkSize, input.len() - ((i - 1) * chunkSize))
        : input.slice(1 + (i - 1) * chunkSize, chunkSize)
    );
  }
  return out;
}

Version 3: Cleaner Variant of V2

A more readable take on V2, and my previous go-to. Performance was solid across engines.

function chunkArrayV3(input, chunkSize) {
  var out = [];
  var ceil = ceiling(input.len() / chunkSize);

  for (var i = 1; i <= ceil; i++) {
    var t = [];
    var offset = (i - 1) * chunkSize;

    if (i == ceil) {
      var c = input.len() - offset < chunkSize ? input.len() - offset : chunkSize;
      for (var x = 1; x <= c; x++) {
        t.push(input[offset + x]);
      }
    } else {
      for (var x = 1; x <= chunkSize; x++) {
        t.push(input[offset + x]);
      }
    }

    out.push(t);
  }

  return out;
}

Version 4: Optimized Looping

An optimized and elegant revision of V3. Slightly better performance across most engines.

function chunkArrayV4(input, chunkSize) {
  var out = [];
  var ln = input.len();

  for (var i = 1; i <= ln; i += chunkSize) {
    var t = [];
    var end = min(i + chunkSize - 1, ln);
    for (var j = i; j <= end; j++) {
      t.append(input[j]);
    }
    out.append(t);
  }

  return out;
}

Version 5: Final Boss

A refined version of V2 — and the top performer across every engine (except Lucee 5).

function chunkArrayV5(input, chunkSize) {
  var out = [];
  var len = input.len();
  var chunkCount = ceiling(len / chunkSize);

  for (var i = 1; i <= chunkCount; i++) {
    var offset = (i - 1) * chunkSize;
    var count = min(chunkSize, len - offset);
    out.append(input.slice(offset + 1, count));
  }

  return out;
}

Benchmark Results

BoxLang
Source: 100,000 elements

chunkArrayV1() - 1455ms
chunkArrayV2() - 39ms
chunkArrayV3() - 687ms
chunkArrayV4() - 663ms
chunkArrayV5() - 27ms

Adobe ColdFusion 2025
Source: 100,000 elements

chunkArrayV1() - 62044ms 🤯
chunkArrayV2() - 23ms
chunkArrayV3() - 93ms
chunkArrayV4() - 41ms
chunkArrayV5() - 3ms 🔥

Lucee 5
Source: 100,000 elements

chunkArrayV1() - 93ms
chunkArrayV2() - 1001ms
chunkArrayV3() - 110ms
chunkArrayV4() - 51ms
chunkArrayV5() - 1001ms

Lucee 6
Source: 100,000 elements

chunkArrayV1() - 124ms
chunkArrayV2() - 5ms
chunkArrayV3() - 81ms
chunkArrayV4() - 60ms
chunkArrayV5() - 4ms


Try It Live