Skip to main content

Indexes

Sometimes JSON input is large and contains elements with IDs and other elements referencing by IDREFs. In such situations it is useful to build indexes to speed up lookups. This can be achieved using the index, indexEach, and indexWith helpers.

If you are familiar with XSLT, then you might know the <xsl:key /> element and key() function. You will immediately recognize the similarities to the indexes in priint Handlebars.

Named indexes are created with the index helper once, and later be used in many instances of indexEach or indexWith.

We will use the below example input for these three helpers:

{
"products": [
{
"id": "P1",
"name": "Laptop",
"categoryId": "C1"
},
{
"id": "P2",
"name": "Mouse",
"categoryId": "C1"
},
{
"id": "P3",
"name": "Desk",
"categoryId": "C2"
}
],
"categories": [
{
"id": "C1",
"name": "Electronics"
},
{
"id": "C2",
"name": "Furniture"
}
]
}

index​

Builds an in-memory lookup index

Usage:

{{index "productsById" match="$.products[*]" use="id"}}

The match parameter is by default a JSONPath to return the array of objects to be indexed. The use parameter is the key to be used for indexing.

Alternatively, you may use XPath 1.0 expressions to select the elements by specifying the mode parameter as xpath.

{{index "productsById" match="//products" use="id" mode='xpath'}}

With the above example, it will create a key-value object in Helper Context memory, so you can access it anywhere. The object in this case will be like this:

Index Representation:

{
"P1": [
{ "id": "P1", "name": "Laptop", "categoryId": "C1" }
],
"P2": [
{ "id": "P2", "name": "Mouse", "categoryId": "C1" }
],
"P3": [
{ "id": "P3", "name": "Desk", "categoryId": "C2" }
]
}

Another example:

{{index "productsByCategory" match="$.products[*]" use="categoryId"}}

So the index representation will be:

{
"C1": [
{ "id": "P1", "name": "Laptop", "categoryId": "C1" },
{ "id": "P2", "name": "Mouse", "categoryId": "C1" }
],
"C2": [
{ "id": "P3", "name": "Desk", "categoryId": "C2" }
]
}

#indexWith​

Gets the FIRST matching indexed item.

Usage:

First, create the index:

{{index "categoriesById" match="$.categories[*]" use="id"}}

Then use

{{#each products}}

<div>
Product:
{{name}}

{{#indexWith "categoriesById" val=categoryId}}
Category:
{{name}}
{{else}}
Category not found
{{/indexWith}}
</div>

{{/each}}

Output:


<div>Product: Laptop Category: Electronics</div>

<div>Product: Mouse Category: Electronics</div>

<div>Product: Desk Category: Furniture</div>

#indexEach​

Loops over ALL matching indexed items.

Usage:

Create index:

{{index "productsByCategory" match="$.products[*]" use="categoryId"}}

Then:

{{#each categories}}

<h2>{{name}}</h2>

<ul>
{{#indexEach "productsByCategory" val=id}}
<li>{{name}}</li>
{{else}}
<li>No products</li>
{{/indexEach}}
</ul>

{{/each}}

Result:

<h2>Electronics</h2>
<ul>
<li>Laptop</li>
<li>Mouse</li>
</ul>

<h2>Furniture</h2>
<ul>
<li>Desk</li>
</ul>

indexEach Special variables​

Like the each block handler indexEach will add loop metadata.

You can use

VariableMeaning
@indexzero-based index
@index_1one-based index
@first"first"
@last"last"
@odd"odd"
@even"even"

Usage:

{{#indexEach "productsByCategory" val=id}}

<div class="{{@odd}} {{@even}} {{@first}} {{@last}}">
{{@index_1}}.
{{name}}
</div>

{{/indexEach}}

Result:


<div class="even first">1. Laptop</div>

<div class="odd">2. Mouse</div>