Jinja Data Structures

Learn about how you can utilize more advanced data structures in Jinja to store larger volumes of variables under one reference name!

Sometimes, you want to store multiple values of the same type and meaning. For example, you have a bunch of items that you want to show to your customer in an email. Creating a variable for each one of the names would be too laborious.

Therefore, Jinja contains more complex data structures to serve various cases like this. These data structures are in essence just a collection of variables stored together under one reference name. But, they also provide you with some useful added functionality. There are three of them available and this article will help you understand the key differences between them.

Lists

The simplest data structure available in Jinja is a list. Lists in Jinja are dynamic data structures, which means that you do not need to know the number of variables that your list will represent. Your list will automatically make itself larger if you need to store more items than it currently allows. Also, you do not need to worry about the data type of your list. Not only can lists store any type of data, but you can also store multiple data types in one list.

Initialising and accessing items in a list

You initialize a list using the {% set myList = %} notation, as with ordinary variables. However, to tell the renderer that you are initializing a list, you need to use the square-brackets notation for lists. In between the square brackets, you can specify the values that your list will contain from the get-go. The values need to be separated by a comma. Initialising an empty list is possible too, just put the square brackets with nothing inside of them. Initializing a list of numbers from 1 to 5 is done through the following command {% set numbersOneToFive = [1,2,3,4,5] %}.

Accessing elements in a list

Lists are also indexed, meaning that every item that is stored in a list, has a special number called an index through which you can access the item. The index of an item indicates the position the item has in the list, beginning from 0. For instance, consider the following list {% set myList = ['a, 'b', 'c', 'x', 'k'] %}. The letter a is the first element in myList, hence, it will have index 0. The letter k is the fifth element, hence it will have index 4. Once you add an element to a list, it will not change its index, unless you remove the elements preceding it.

You can use indexing for easy access to the elements from your list using square brackets notation. If you want to retrieve some element from the list and you know its index, you can simply use {{ myList[0] }} and you will get the value of the first item of myList list.

๐Ÿ“˜

Indexing from 0

Keep in mind that lists are indexed from 0. It is a common mistake to refer to the first element with index 1, but that is actually the second element.

Adding and removing items from a list

Because lists are dynamic data structures, you can add and remove elements from lists in your jinja template code.

Adding an element to a list

You can add an element to a list using the append method. Because we wanted to avoid using the underlying python Language, the notation in Bloomreach Engagement somewhat differs from the one officially supported in Jinja. Instead of myList.append(myValue) you will need to use append myValue to myList. Nevertheless, the functionality does not change. Your item will still be added to the end of the list.

๐Ÿ“˜

Removing more than one element

Note that the index function only returns the index of the first occurrence of your value. If you can't be sure that the value is in your list or if there can be more then one instance of your value, you will need to use either flow control or filtering. This will be covered in the following sections.

{# Initialise myList with some values #}
{% set myList = [1,5,3,4,2] %}
{# add 5 to myList #} 
{% append 5 to myList %}
{# remove the second item from the list #} 
{% set temp = myList.pop(1) %}
{# find the index of the number 2 in myList #}
{% set myIndex = myList.index(2) %}
{# pop the number two from myList #} 
{% set temp = myList.pop(myIndex) %}
{# output the list and index of 1 #} 
{{ "values in my list:"}}
{{ myList }}
{% set indexOne = myList.index(1) %} 
{{ "</br> index of the number one:" }}
{{ indexOne }}
        
Output:
values in my list: [1,3,4,5]
index of the number one: 0

Tuples

Another data structure available in Jinja is a tuple. In simple terms, similarly to lists, tuples are also a bunch of values that are referred to by a common name and they are not data-type specific. However, unlike lists, tuples are immutable, which means that once you define a tuple, you can only access its values but cannot change them. In simple words, tuples are "read-only".

Defining a tuple

You define a tuple by using round brackets. For instance, {% set myPair = ("dog", "cat") %} will be a tuple containing two values, "dog" and "cat". As with lists, tuples are also indexed beginning from 0.
Hence, if you want to output the second value "cat" from myPair, I will use {{ myPair[1] }}.

Tuples are usually used to store two or more values. But, if you want to create a singleton, a tuple with only one value, you need to put a comma after your value, as in the following example {% set mySingleton = ("myString",) %}.

{# Creating a list of URL adresses with captions #} 
{# Create an empty list for storing my URLs #} 
{% set myUrls = [] %}
{# append some URLs to myList #} 
{% append ("index.html", "Landing page") to myUrls %}
{% append ("cart.html", "Cart") to myUrls %}
{# Output the second value of the second tuple in my list #} 
{{ myUrls[1][1] }}

Output: Cart

Dictionaries

The last data structure available in our implementation of Jinja is a dictionary. As with lists, they are used to store multiple values of arbitrary data types. The main difference comes in the way you can retrieve the values from dictionaries. Dictionaries are not indexed. Instead, they store your values as a key-value pair. To retrieve the value from your dictionary, you simply use the key that you stored the value with.

Defining and using a dictionary

To define a dictionary, you need to use curly brackets: {% set myDict = ({"Animal" : "Duck", "Breed" : "American Pekin"}) %} . Later, you can access your values by simply invoking {{ myDict["Animal"] }} which will return the value Duck. You don't need to set your keys to be of type String as in the example, you can also use integers, floats, or even the value none.

{# initialise the dictionary #}
{% set customer = ({"Name":"Peter","Surname": "Smith"}) %}
{# Output the value stored by the key Name #} 
{{ customer["Name"] }}

Output: Peter

Data structures as data types

Data structures can be treated as data types themselves. This allows you to not only data structures containing variables of the primitive data types covered in the basics, but also combine them together. In fact, one such combination was already used in the example for tuples. This yields you a lot of power while working in Bloomreach Engagement. For instance, each row of your catalog can be treated as a dictionary, with the names of columns serving as keys. Then, if you want to work with your catalog data in Jinja, you can create a list of dictionaries or even a dictionary of dictionaries with the keys being the ids of the rows and the values being the rows themselves.

Iterables

All of the data structures with the addition of strings are iterable in Jinja2. This means that for each of the data types you can iterate through their content. Strings are iterated through the characters they contain, Pairs and lists through the values they contain and finally, dictionaries are iterated through their keys.

The primary key for accessing items of Iterables (Strings, Lists, Tuples, Dictionaries) must be a constant. Therefore, when accessing their content dynamically, Jinja throws an error. To get around this, wrap your data structure in another List, Tuple or Dictionary. Firstly, wrap your data structure in the square-brackets. Then, access your item from the wrapped data structure using [myDataStructure][0][myVariable].