Design

Loop over array

Repeat any part of your document
COMMUNITY FEATURE Available for:
Carbone Cloud
Carbone On-premise
Embedded Carbone JS
  v2.0+ 

Overview

Carbone repeats any part of a document (rows, titles, pages...) from an array in your data.

Mark the pattern with two consecutive items: [i] on the first, [i+1] on the next. Carbone takes the [i] item as the example, automatically detects the block to repeat, outputs one copy per array element, and removes the [i+1] marker row before rendering.

An empty array renders nothing: both the [i] and [i+1] rows are removed.

Simple array

In this example, we want to loop over an array of cars. This is a vertical loop: Carbone repeats the pattern downward, adding one row per array item.

data
{
  "cars" : [
    {"brand" : "Toyota" , "id" : 1 },
    {"brand" : "Hyundai", "id" : 2 },
    {"brand" : "BMW"    , "id" : 3 },
    {"brand" : "Peugeot", "id" : 4 }
  ]
}
template
Carsid{d.cars[i].brand}{d.cars[i].id}{d.cars[i+1].brand}
Carbone Merge Icon
result
CarsidToyota1Hyundai2BMW3Peugeot4

Only one [i+1] tag is enough to find the repetition pattern. It is not necessary to repeat the whole [i+1] part.

Get inspired by one of our real-life examples: Menu, Simple Planning or Best Manufacturer Awards

Nested arrays

Carbone manages nested arrays (unlimited depth). Here is an example where a whole portion of a document is repeated.

data
[
  {
    "brand": "Toyota",
    "models": [{ "size": "Prius 4", "power": 125 }, { "size": "Prius 5", "power": 139  }]
  },
  {
    "brand": "Kia",
    "models": [{ "size": "EV4", "power": 450  }, { "size": "EV6", "power": 500  }]
  }
]
template
{d[i].brand}Models{d[i].models[i].size} - {d[i].models[i].power}{d[i].models[i+1]}{d[i+1]}
Carbone Merge Icon
result
ToyotaModelsPrius 4 - 125Prius 5 - 139KiaModelsEV4 - 450EV6 - 500

Notice that the whole first block is not repeated: only the closing title # {d[i+1]} is needed. This single [i+1] marker is enough for Carbone to detect where the outer loop, on the root array d, ends.

Get inspired by one of our real-life examples: Best Selling Vehicles Awards or Stock Inventory Spreadsheet

Horizontal loops (repeat columns)

A horizontal loop repeats columns instead of rows. Mark the [i] and [i+1] positions on two consecutive columns of the same row, and Carbone adds one column per array item, growing the table to the right. One difference with the vertical loop matters: it closes the whole block with a single [i+1], while a horizontal loop repeats within each row, so every marked row needs its own [i+1] end-marker (a bare {d.array[i+1]} is enough).

Example

Here we turn a list of products into columns, one column per product. The first column holds static labels, one per row. The loop fills the rest, adding one product column per array item as it grows to the right:

data
{
  "products": [
    { "name": "Bike",  "price": 200, "stock": 12, "category": "Outdoor" },
    { "name": "Chair", "price": 40,  "stock": 30, "category": "Office"  },
    { "name": "Lamp",  "price": 25,  "stock": 54, "category": "Home"    }
  ]
}
template
Name{d.products[i].name}{d.products[i+1]}Price{d.products[i].price}{d.products[i+1]}Stock{d.products[i].stock}{d.products[i+1]}Category{d.products[i].category}{d.products[i+1]}
Carbone Merge Icon
result
NameBikeChairLampPrice2004025Stock123054CategoryOutdoorOfficeHome

Get inspired by one of our real-life examples: Product Comparison Table or Sensor readings

Bi-directional loop

v4.8.0+

The bidirectional loop performs iterations in 2 directions, creating additional columns and rows.

There are a few restrictions:

data
{
  "titles" : [{ "name": "Kia" }, { "name": "Toyota" }, { "name": "Hopium" }],
  "cars"   : [
    { "models" : [ "EV3", "Prius 1", "Prototype" ] },
    { "models" : [ "EV4", "Prius 2", "" ]          },
    { "models" : [ "EV6", "Prius 3", "" ]          }
  ]
}
template
{d.titles[i].name}{d.titles[i+1].name}{d.cars[i].models[i]}{d.cars[i].models[i+1]}{d.cars[i+1].models[i]}
Carbone Merge Icon
result
KiaToyotaHopiumEV3Prius 1PrototypeEV4Prius 2EV6Prius 3

Get inspired by one of our real-life examples: Store Inventory or Product Comparison Table

Access the loop iterator value

v4.0.0+

Access the iterator value when a list is printed in a document. For example: {d[i].cars[i].other.wheels[i].tire.subObject:add(.i):add(..i):add(...i)}

The number of dots corresponds to the position of the i in the hierarchy:

The number of dots is currently inverted. It should be ...i for d[i] and .i for wheels[i]. This is a known bug. However, many users rely on the current behavior, and fixing it would break some reports. We plan to address this issue in the future while ensuring backward compatibility.

Get inspired by one of our real-life examples: Summary

Parallel loop

Iterate through two separate arrays at the same time using the same iterator variable.

As seen earlier, the iterator i can be used in formatters.
By combining it with relative path access, we can navigate back in the JSON hierarchy using two dots (..).
This allows us to print the content of another array, brands, using the same iterator as the cars array.

data
{
  "cars" : [
    { "id" : 1 },
    { "id" : 2 },
    { "id" : 3 },
    { "id" : 4 }
  ],
  "brands" : [
    { "name" : "Toyota" },
    { "name" : "Hyundai"},
    { "name" : "BMW"    }
  ]
}
template
Carsid{d.cars[i].id}{d.cars[i].id:print(..brands[.i].name)}{d.cars[i+1].id}
Carbone Merge Icon
result
Carsid1Toyota2Hyundai3BMW4

Array of string/number loop

v4.9.0+

When an array holds plain strings or numbers instead of objects, loop on the value itself with {d.colors[i]}, without a property name.

data
{
  "colors": ["Red", "Green", "Blue"]
}
template
Colors{d.colors[i]}{d.colors[i+1]}
Carbone Merge Icon
result
ColorsRedGreenBlue

Looped values accept formatters like any other tag, for example {d.colors[i]:upperCase}.