1
0

More than 1 year has passed since last update.

Ansible Tips: list of dictionaryとdictionaryを相互に変換する (loopを使用しないロジックに更新)

Last updated at Posted at 2020-10-01

2021/12: 1階層の変換をloopを使用しないロジックに置き換え

dictonaryのlistを、dictonary型に変換したり、戻したりする例

それぞれのデータ型はメリット、デメリットが存在する。

参考

例えばdictonary型ではdeep mergeが使えるのでデータの統合が容易、など
ここでは、それらを相互に変換するロジックを考える。

1階層のデータ

データ

以下の様なdictonaryのlistを

- name: foo1
  attr: bar1
- name: foo2
  attr: bar2

以下の様なdictonaryと相互に変換することを考える

foo1:
  name: foo1
  attr: bar1
foo2:
  name: foo2
  attr: bar2
  • ただしdictionaryでは順序の保証がないので、listに戻した際にも順序は保証されない。

playbook例

site.yml
---
- hosts: all
  vars:

    hoge:
      - name: foo1
        attr: bar1
      - name: foo2
        attr: bar2

    ansible_python_interpreter: '/usr/bin/python3'

  tasks:
    - name: 元データ
      debug:
        var: hoge

    - name: list of dict型をdict型に変換
      set_fact:
        hoge_dict: '{{ dict( hoge|map(attribute="name")|zip(hoge) ) }}'

    - name: dict型に変換後
      debug:
        var: hoge_dict

    - name: dict型からlist of dict型に戻す
      set_fact:
        hoge_dict2list: '{{ hoge_dict.values() }}'

    - name: list of dict型に変換後
      debug:
        var: hoge_dict2list
$ ansible-playbook -i localhost, -c local site.yml 

PLAY [all] ***************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************
ok: [localhost]

TASK [元データ] **********************************************************************************************************************************************
ok: [localhost] => {
    "hoge": [
        {
            "attr": "bar1",
            "name": "foo1"
        },
        {
            "attr": "bar2",
            "name": "foo2"
        }
    ]
}

TASK [list of dict型をdict型に変換] **************************************************************************************************************************
ok: [localhost]

TASK [dict型に変換後] ****************************************************************************************************************************************
ok: [localhost] => {
    "hoge_dict": {
        "foo1": {
            "attr": "bar1",
            "name": "foo1"
        },
        "foo2": {
            "attr": "bar2",
            "name": "foo2"
        }
    }
}

TASK [dict型からlist of dict型に戻す] ************************************************************************************************************************
ok: [localhost]

TASK [list of dict型に変換後] ********************************************************************************************************************************
ok: [localhost] => {
    "hoge_dict2list": [
        {
            "attr": "bar1",
            "name": "foo1"
        },
        {
            "attr": "bar2",
            "name": "foo2"
        }
    ]
}

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


2階層のデータ

with_subelements が使える2階層の場合、list of dict -> dictはinclude_tasksなどを使わなくとも変換可能

tasks例: 2階層list of dict -> dict

site.yml
---
- hosts: all
  gather_facts: false
  vars:

    hoge2:
    - name: foo1
      attr: foo1_attr
      member:
      - name: bar11
        attr: baz11
      - name: bar12
        attr: baz12
    - name: foo2
      attr: foo2_attr
      member:
      - name: bar21
        attr: baz21
      - name: bar22
        attr: baz22

  tasks:
  - set_fact: { fuga2: {} } # loopで出力を追加していく初期値
  - vars:                   # task変数定義
      index1: member        #   2階層目を示すkey
      key0: name            #   1階層目の要素を識別するキー。dictのキーとして使用
      key1: name            #   2階層目の要素を識別するキー。dictのキーとして使用
    with_subelements:       # 2階層ループ
    - "{{ hoge2 }}"         #   対象のlist of dict
    - "{{ index1 }}"        #   2階層目を示すkey。task変数で指定されたもの
    set_fact:
      fuga2: "{{ 
        fuga2 
        | combine( 
          { (item[0][key0]): item[0] } 
        | combine( 
          { (item[0][key0]): 
            { index1: 
              { (item[1][key1]): item[1] } 
            } 
          }
        , recursive=true)
        , recursive=true) 
        }}"

  - debug:
      var: fuga2
  • loopで、空dictのfuga2にdictを追加していく
  • |combine()にてdict型をマージ

実行例

TASK [set_fact] *****************************************************************************************************
ok: [localhost] => (item=[{'name': 'foo1', 'attr': 'foo1_attr'}, {'name': 'bar11', 'attr': 'baz11'}])
ok: [localhost] => (item=[{'name': 'foo1', 'attr': 'foo1_attr'}, {'name': 'bar12', 'attr': 'baz12'}])
ok: [localhost] => (item=[{'name': 'foo2', 'attr': 'foo2_attr'}, {'name': 'bar21', 'attr': 'baz21'}])
ok: [localhost] => (item=[{'name': 'foo2', 'attr': 'foo2_attr'}, {'name': 'bar22', 'attr': 'baz22'}])

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "fuga2": {
        "foo1": {
            "attr": "foo1_attr",
            "member": {
                "bar11": {
                    "attr": "baz11",
                    "name": "bar11"
                },
                "bar12": {
                    "attr": "baz12",
                    "name": "bar12"
                }
            },
            "name": "foo1"
        },
        "foo2": {
            "attr": "foo2_attr",
            "member": {
                "bar21": {
                    "attr": "baz21",
                    "name": "bar21"
                },
                "bar22": {
                    "attr": "baz22",
                    "name": "bar22"
                }
            },
            "name": "foo2"
        }
    }
}

tasks例: 2階層 dict -> list of dict

2階層 dictからlist of dictへの戻し

playbook続き

   - set_fact: { out_list: [] }
   - vars:
      in_dict: "{{ fuga2 }}"
      index1: member
    loop: "{{ in_dict|dict2items|map(attribute='value')|list }}"
    set_fact:
      out_list: "{{ out_list + [ 
        item
        |combine(
          { index1: item[index1]|dict2items|map(attribute='value')|list } 
        , recursive=true)
        ] 
        }}"

  - debug:
      var: out_list
  • loopで、空listのout_listに要素を追加していく
  • + [xxxx] にてlist型に要素を追加

出力続き

TASK [set_fact] *****************************************************************************************************
ok: [localhost] => (item={'name': 'foo1', 'attr': 'foo1_attr', 'member': {'bar11': {'name': 'bar11', 'attr': 'baz11'}, 'bar12': {'name': 'bar12', 'attr': 'baz12'}}})
ok: [localhost] => (item={'name': 'foo2', 'attr': 'foo2_attr', 'member': {'bar21': {'name': 'bar21', 'attr': 'baz21'}, 'bar22': {'name': 'bar22', 'attr': 'baz22'}}})

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "out_list": [
        {
            "attr": "foo1_attr",
            "member": [
                {
                    "attr": "baz11",
                    "name": "bar11"
                },
                {
                    "attr": "baz12",
                    "name": "bar12"
                }
            ],
            "name": "foo1"
        },
        {
            "attr": "foo2_attr",
            "member": [
                {
                    "attr": "baz21",
                    "name": "bar21"
                },
                {
                    "attr": "baz22",
                    "name": "bar22"
                }
            ],
            "name": "foo2"
        }
    ]
}

out_listをYAMLで書くと

---
- attr: foo1_attr
  member:
  - attr: baz11
    name: bar11
  - attr: baz12
    name: bar12
  name: foo1
- attr: foo2_attr
  member:
  - attr: baz21
    name: bar21
  - attr: baz22
    name: bar22
  name: foo2
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0