Alex K
Alex K

Reputation: 13

Convert list of objects (string, string) into Dictionary (string, list<string>) in Ansible

I have a list of objects (each object contains two attributes of type string) which I need to convert into Dictionary. The first attribute of the object will become a key, the second attribute of the object would have to be combined into a list of strings.

I went through several solutions but couldn't find exactly what I need.

Input Example:

[
    {
        "name": "AAA",
        "value": "111"
    },
    {
        "name": "AAA",
        "value": "222"
    },
    {
        "name": "BBB",
        "value": "333"
    },
    {
        "name": "BBB",
        "value": "444"
    },
    {
        "name": "CCC",
        "value": "555"
    }
]

Desired Outputs (either one works):

[
    {
        "name": "AAA",
        "value": [ "111", "222" ]
    },
    {
        "name": "BBB",
        "value": [ "333", "444" ]
    },
    {
        "name": "CCC",
        "value": [ "555" ]
    }
]
[
    "AAA": [ "111", "222" ],
    "BBB": [ "333", "444" ],
    "CCC": [ "555" ]
]

Upvotes: 1

Views: 2799

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68034

The tasks below

- set_fact:
    my_list: "{{ my_list|default([]) +
                 [{item.0: item.1|json_query('[].value')}] }}"
  loop: "{{ input|groupby('name') }}"
- debug:
    var: my_list

give

"my_list": [
    {
        "AAA": [
            "111", 
            "222"
        ]
    }, 
    {
        "BBB": [
            "333", 
            "444"
        ]
    }, 
    {
        "CCC": [
            "555"
        ]
    }
]

Upvotes: 2

Zeitounator
Zeitounator

Reputation: 44615

This is how I would approach the problem. The solution is mainly based on the use of the json_query filter

First, you need a list of all unique names in the list of objects so you can loop over it and extract the corresponding values. This is done by filtering the input variable with the following jinja2 template expresion {{ input | json_query("[].name") | unique }}

Now we just have to loop over that list and filter the input variable again. This time we want to extract a flat list of all values for all objects having the given name. This is done with the following example jinja2 expression: {{ input | json_query("[?name=='a_unique_name'].value") }}

The rest is just a matter of setting facts correctly, either in a new list or hashmap as your requested. I made both in the following example playbook.

Note: I used yaml folded blocks with white space control (>-) in this example to ease writing (limiting quotes escaping...) and reading (long lines split...). If you are not familiar with them you can check https://yaml-multiline.info/ for a complete overview of all options.

The playbook

---
- name: Rearrange var example
  hosts: localhost
  gather_facts: false

  vars:

     input: [
      { "name": "AAA", "value": "111" },
      { "name": "AAA", "value": "222" },
      { "name": "BBB", "value": "333" },
      { "name": "BBB", "value": "444" },
      { "name": "CCC", "value": "555" }
     ]

  tasks:

    - name: Create list and dict vars
      vars:
        jsquery: >-
          [?name=='{{ item }}'].value
      set_fact:
        list_var: >-
          {{
            list_var | default([])
            +
            [{'name': item, 'value': input | json_query(jsquery)}]
          }}
        dict_var: >-
          {{
            dict_var | default({})
            | combine ({ item: input | json_query(jsquery)})
          }}
      loop: "{{ input | json_query('[].name') | unique }}"


    - name: Show calculated list and dict vars
      debug:
        var: "{{ item }}"
      loop:
        - list_var
        - dict_var

Which gives:

PLAY [Rearrange var example]  ************************************************

TASK [Create list and dict vars] *********************************************
ok: [localhost] => (item=AAA)
ok: [localhost] => (item=BBB)
ok: [localhost] => (item=CCC)

TASK [Show calculated vars] **************************************************
ok: [localhost] => (item=list_var) => {
    "ansible_loop_var": "item",
    "item": "list_var",
    "list_var": [
        {
            "name": "AAA",
            "value": [
                "111",
                "222"
            ]
        },
        {
            "name": "BBB",
            "value": [
                "333",
                "444"
            ]
        },
        {
            "name": "CCC",
            "value": [
                "555"
            ]
        }
    ]
}
ok: [localhost] => (item=dict_var) => {
    "ansible_loop_var": "item",
    "dict_var": {
        "AAA": [
            "111",
            "222"
        ],
        "BBB": [
            "333",
            "444"
        ],
        "CCC": [
            "555"
        ]
    },
    "item": "dict_var"
}

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


Upvotes: 1

Related Questions