Search code examples
ansiblejinja2python-3.5ansible-2.xansible-template

Map function over values of dict in ansible jinja template


I want to convert the values of a dictionary into strings and lowercase them, then map over the dict to join each item pair with an =, and then join the items with a ,.

Example input:

{"A": "Aardvark", "B": "Beaver", "C": "Cat"}

Desired output:

A=aardvark,B=beaver,C=cat

I actually don't care about the order that A, B, C arrive in, but I want to make sure that I the keys and values don't get mixed up in the end, i.e., no A=cat or whatever.

What I'm doing now is this:

{{ foo.keys() | zip(foo.values() | map("lower")) | map("join", "=") | join(",") }}

It works so far with the test data I have given it. However, I am not sure that this is guaranteed to be correct, nor do I think this is the most efficient or elegant way to solve this. Is there a better way to map the lower function over the values in the dict?

I tried using dict2items and specifying an attribute on the map function, but I get an error about an unexpected keyword arg when I do this, plus it's really ugly:

{{ (foo | dict2items | map("lower", attribute="value") | list | items2dict).items() | map("join", "=") | join(",") }}

Note: I am using Ansible 2.9 on Python 3.5 right now, but would really like a solution that works regardless of the Python version.


Solution

  • You could use a custom filter.

    Create a filter_plugins/ folder in your playbook folder (I have named the file myfilters.py and the filter cstring)

    myfilters.py in folder filter_plugins/:

    #!/usr/bin/python
    class FilterModule(object):
        def filters(self):
            return {
                'cstring': self.cstring
            }
        
        def cstring(self, data):
            result = ""
            for k in data:
                result += (',' if result else '') + k + '=' + data[k].lower()
        
            return result
    

    playbook:

    - name: "tips1"
      hosts: localhost
      vars:
        foo: {"A": "Aardvark", "B": "Beaver", "C": "Cat"}
      tasks:
        - name: debug users      
          set_fact:
            result: "{{ foo | cstring }}"
      
        - name: display result     
          debug:
            var: result 
    

    result:

    ok: [localhost] => {
        "result": "A=aardvark,B=beaver,C=cat"
    }
    

    The advantage of using a custom filter is to do complex things or lot of operations with only one action.