Manuel Rodriguez
Manuel Rodriguez

Reputation: 73

How do I convert a list of strings to a list of dictionaries?

I have a list of strings that looks like this:

"forwarded_ports": [
    "123",
    "234",
    "456"
]

And I'd like to use the values of this list as values in a list of dictionaries that would look like this:

"mapped_ports": [
    {
        "external_port": "123",
        "internal_port": "123"
    },
    {
        "external_port": "234",
        "internal_port": "234"
    },
    {
        "external_port": "456",
        "internal_port": "456"
    }
]

Is there a way to do this using inline Ansible/Jinja filters? I know I can do this easily with an Ansible loop like this:

- set_fact:
    mapped_ports: "{{ mapped_ports| default([]) + [{ 'internal_port' : item , 'external_port' : item }] }}"
  loop: "{{ forwarded_ports }}"

But ideally this data structure would be created on the fly in a defaults file with just filters, etc. It seems like there's a simple way to do it with map and dict filters but I can't seem to figure it out. Thanks for any help!

Upvotes: 3

Views: 556

Answers (2)

mdaniel
mdaniel

Reputation: 33203

Ordinarily, I don't recommend json_query but in this case it's a great fit for what you're trying to do because of its ability to construct new nodes via projection:

- debug:
    msg: >-
      {{ forwarded_ports | json_query("[*].{external_port: @, internal_port: @}") }}
  vars:
    "forwarded_ports": [
      "123",
      "234",
      "456"
    ]

yields

ok: [localhost] => {
    "msg": [
        {
            "external_port": "123",
            "internal_port": "123"
        },
        {
            "external_port": "234",
            "internal_port": "234"
        },
        {
            "external_port": "456",
            "internal_port": "456"
        }
    ]
}

Upvotes: 4

Zeitounator
Zeitounator

Reputation: 44635

In a nutshell (but I don't really recommend it as it is using a var as a full jinja2 template and I would stick to your actual set_fact which is probably more legible for most users...):

---
- hosts: localhost
  gather_facts: false

  vars:
    forwarded_ports:
      - 123
      - 234
      - 456

    mapped_ports: >-
      {%- set mapped=[] -%}
      {%- for port in forwarded_ports -%}
        {{ mapped.append({'external_port': port, 'internal_port': port}) }}
      {%- endfor -%}
      {{ mapped }}

  tasks:
    - debug:
        var: mapped_ports

Which gives:

$ ansible-playbook my_playbook.yml

PLAY [localhost] **************************************************************************************************************************************************************************************************************

TASK [debug] ******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "mapped_ports": [
        {
            "external_port": 123,
            "internal_port": 123
        },
        {
            "external_port": 234,
            "internal_port": 234
        },
        {
            "external_port": 456,
            "internal_port": 456
        }
    ]
}

PLAY RECAP ********************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Upvotes: 3

Related Questions