Loop indexing

Loop counting is provided for free, yielding an index of the current iteration of the loop. As variables, this can be accessed in a few different ways. The following table outlines the ways they can be referenced:

Variable Description
loop.index The current iteration of the loop (1 indexed)
loop.index0 The current iteration of the loop (0 indexed)
loop.revindex The number of iterations until the end of the loop (1 indexed)
loop.revindex0 The number of iterations until the end of the loop (0 indexed)
loop.first Boolean True if the first iteration
loop.last Boolean True if the last iteration
loop.length The number of items in the sequence

 

Having information related to the position within the loop can help with logic around what content to render. Considering our previous examples, instead of rendering multiple lines of data_dir to express each data directory, we could instead provide a single line with comma-separated values. Without having access to loop iteration data, this would be difficult, but by using this data, it can be fairly easy. For the sake of simplicity, this example assumes a trailing comma after the last item is allowed, and that white space (new lines) between items is also allowed:

# data dirs
{% for dir in data_dirs if dir != "/" -%}
{% if loop.first -%}
data_dir = {{ dir }},
{% else -%}
{{ dir }},
{% endif -%}
{% else -%}
# no data dirs found
{% endfor -%}

The preceding example made use of the loop.first variable to determine whether it needed to render the data_dir = part, or if it just needed to render the appropriately spaced padded directory. By using a filter in the for statement, we get a correct value for loop.first, even if the first item in data_dirs is the undesired /. To test this, we'll once again modify demo-for.j2 with the updated template and modify template-demo-for.yaml to define some data_dirs, including one of /, which should be filtered out:

--- 
- name: demo the template 
  hosts: localhost 
  gather_facts: false  
  vars: 
    data_dirs: ['/', '/foo', '/bar']  
  tasks: 
    - name: pause with render 
      pause: 
        prompt: "{{ lookup('template', 'demo-for.j2') }}"

Now, we can execute the playbook and see our rendered content, as follows:

If in the preceding example trailing commas were not allowed, we could utilize inline if statements to determine whether we're done with the loop and render commas correctly, as shown in the following example:

# data dirs. 
{% for dir in data_dirs if dir != "/" -%} 
{% if loop.first -%} 
data_dir = {{ dir }}{{ ',' if not loop.last else '' }} 
{% else -%} 
           {{ dir }}{{ ',' if not loop.last else '' }} 
{% endif -%} 
{% else -%} 
# no data dirs found 
{% endfor -%}

Using inline if statements allows us to construct a template that will only render a comma if there are more items in the loop that passed our initial filter. Once more, we'll update demo-for.j2 with the earlier content and execute the playbook:

The output is much the same as before, except this time, our template evaluates whether to place a comma after each value of dir in the loop using the inline if, removing the stray comma at the end of the final value.