falsePockets
falsePockets

Reputation: 4293

How to apply dictionary to list of items with Jinja filter?

I have a list of items, which all appear as keys in a certain dictionary. I would like to use Jinja2 filters to lookup each list item in the dictionary and return the corresponding value, in a list.

In python this would be:

[my_dict[x] for x in my_list]

What is the Jinja equivalent?

my_list | map(my_dict) does not work.

Here's a sample playbook.

---
- hosts: localhost
  connection: local
  vars:
    my_dict:
      a: 1
      b: 2
      c: 3
    my_list:
      - a
      - c
  tasks:
  - assert:
      that: 
      - "{{ actual == expected }}"
    vars:
      # [my_dict[x] for x in my_list]
      actual: "{{ my_list | map(my_dict) | list }}"
      expected:
        - 1
        - 3

If you run this, you get:

fatal: [localhost]: FAILED! => {"msg": "An unhandled exception occurred while templating '{{ my_list | map(my_dict) | list }}'. Error was a <class 'ValueError'>, original message: key must be a string"}

I want to modify the actual: line so that this playbook runs without error.

Note that I do not want to loop in Ansible itself. This is a simple MWE. In my real example, this lookup should be inline inside a much larger template file.

Upvotes: 2

Views: 2345

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 67994

Given the list

  my_list: [a, c]

Use filter extract

  actual: "{{ my_list|map('extract', my_dict)|list }}"

should give

  actual: [1, 3]

The above solution doesn't work when some items in the list are missing in the dictionary. For example,

  my_list: [a, c, x]

will fail

''dict object'' has no attribute ''x'''

In this case, select the keys that are in the list. The expression below gives the same result

  actual: "{{ my_dict|dict2items|
                      selectattr('key', 'in', my_list)|
                      map(attribute='value')|list }}"

Example of a complete playbook for testing

- hosts: localhost

  vars:

    my_list: [a, c]
    my_dict:
      a: 1
      b: 2
      c: 3
    actual1: "{{ my_list|map('extract', my_dict)|list }}"
    actual2: "{{ my_dict|dict2items|
                         selectattr('key', 'in', my_list)|
                         map(attribute='value')|list }}"

  tasks:

    - debug:
        var: actual1|to_yaml
    - debug:
        var: actual2|to_yaml

Upvotes: 4

Related Questions