Search code examples
ansiblejinja2

Formatting a list of items in Ansible/Jinja2


Is it possible to convert a list of lists/dicts to a list of strings using a formatting string in Ansible / Jinja2?

I understand I can do something like:

{{["First: %d", "Second: %d"] | map("format", 1) | join(", ") }}

To get First: 1, Second 1.

Would it be possible to do something like

{{[[1, 1], [2, 2]] | map("format", "Num %d, %d") | join(", ") }}

and result in Num 1, 1, Num 2, 2?


Solution

  • It is not possible with the core format filter. But if you are willing to write a few lines of python, you can easily solve this with a custom filter. I went to the simplest for demo, you would probably have to harden the code and fix edge cases for a real life use. You may want to have a look at the plugin development documentation for more info.

    For this example, I will save the custom file in the filter_plugins directory adjacent to my test playbook. You can save it in a collection or a role if you want to share across different projects.

    In filter_plugins/my_format_filters.py

    
    def reverse_format(param_list, format_string):
        """format format_sting using elements in param_list"""
        return format_string % tuple(param_list)
    
    
    class FilterModule(object):
        """my format filters."""
    
        def filters(self):
            """Return the filter list."""
            return {
                'reverse_format': reverse_format
            }
    

    Then the following example playbook:

    ---
    - name: custom filter demo
      hosts: localhost
      gather_facts: false
    
      tasks:
        - name: map custom reverse_format filter
          debug:
            msg: '{{ item.replacements | map("reverse_format", item.format) | join(", ") }}'
          loop:
            - replacements:
                - [1, 1]
                - [2, 2]
              format: "Num %d, %d"
            - replacements:
                - ['Jack', 'John', 12]
                - ['Mary', 'Alicia', 34]
              format: "%s owes %s %d€"
    

    gives:

    PLAY [custom filter demo] **************************************************************************************************************************************************************************************************************
    
    TASK [map custom reverse_format filter] ************************************************************************************************************************************************************************************************
    ok: [localhost] => (item={'replacements': [[1, 1], [2, 2]], 'format': 'Num %d, %d'}) => {
        "msg": "Num 1, 1, Num 2, 2"
    }
    ok: [localhost] => (item={'replacements': [['Jack', 'John', 12], ['Mary', 'Alicia', 34]], 'format': '%s owes %s %d€'}) => {
        "msg": "Jack owes John 12€, Mary owes Alicia 34€"
    }
    
    PLAY RECAP *****************************************************************************************************************************************************************************************************************************
    localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0