[philiptellis] /bb|[^b]{2}/
Never stop Grokking


Wednesday, May 14, 2025

Ansible: Extracting multiple attributes from a list of dicts

I've been writing a bunch of ansible playbooks, and in one case I had to transform a list of dicts to extract two attributes from each dict and create a new list of dicts. i.e., given a list like this:

entities:
  - id: 123
    label: Label 1
    type: foo
    status: enabled
  - id: 234
    label: Label 2
    type: foo
    status: enabled
  - id: 345
    label: Label 3
    type: bar
    status: enabled

I need to transform it into this:

entities:
  - id: 123
    type: foo
  - id: 234
    type: foo
  - id: 345
    type: bar

I found the examples in the ansible docs to be very limited. In most cases there are no examples that show the use of additional parameters to filters. I defintely couldn't find anything that would let me extract two attributes from a list of dicts. There are examples to extract a single element using map(attribute='xxx'), but nothing to extract more than one attribute, so I had to come up with something of my own.

I ended up with two possible solutions depending on how much flexibility you have in your playbook.

1. Using loop

The easier option is to use loop and construct the new list one element at a time. You can do this if you have the option to use a set_fact block separate from where you need to use the variable.

- set_fact:
    entities_transformed: '{{ entities_transformed|d([]) + [{"id": item.id, "type": item.type}] }}'
  loop: '{{ entities }}'

This set_fact block creates a new fact called entities_transformed by repeatedly appending each transformed element to a new list.

2. As a one liner

If you need to write it all as a one liner without a set_fact, then this second approach works for you.

  entities_transformed: '{{ entities
                            | map("dict2items")
                            | map("selectattr", "key", "in", ["id", "type"])
                            | map("items2dict") }}'

This works in multiple steps and I'll explain each with what the output looks like at that stage.

map("dict2items")

This transforms the entities list into the following:

  - - key: id
      value: 123
    - key: label
      value: Label 1
    - key: type
      value: foo
    - key: status
      value: enabled
  - - key: id
      value: 234
    - key: label
      value: Label 2
    - key: type
      value: foo
    - key: status
      value: enabled
  - - key: id
      value: 345
    - key: label
      value: Label 3
    - key: type
      value: bar
    - key: status
      value: enabled
map("selectattr", "key", "in", ["id", "type"])

This strips down to the required keys:

  - - key: id
      value: 123
    - key: type
      value: foo
  - - key: id
      value: 234
    - key: type
      value: foo
  - - key: id
      value: 345
    - key: type
      value: bar
map("items2dict")

This reverses the first step giving us the following:

  - id: 123
    type: foo
  - id: 234
    type: foo
  - id: 345
    type: bar

Both options work equally well, but I prefer the second because it avoids creating additional facts and requiring loops in places where I cannot use one.

0 comments :

Post a Comment

...===...