3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ansible Tips: dict 構造においてキー部分に対する文字列操作をフィルターやjinja2 loopにて行う

Last updated at Posted at 2020-09-04

備忘

課題

以下の様なリストにおいて、各dictのキー部分を文字列操作したい。
例えば以下のキーを小文字化する例を考える。

    - KEY1: Val1
      KEY2: Val2
      KEY3: Val3
    - KEY1: Val4
      KEY2: Val5
      KEY3: val6

    - key1: Val1
      key2: Val2
      key3: Val3
    - key1: Val4
      key2: Val5
      key3: Val6

ソリューション例

キーと値を一旦分離し、keyに対してフィルターなりで変更を加え、zipフィルターで再び合わせる。

サンプルplaybook

---
- hosts: localhost
  gather_facts: false
  vars:
    foo:
    - KEY1: Val1
      KEY2: Val2
      KEY3: Val3
    - KEY1: Val4
      KEY2: Val5
      KEY3: Val6
  tasks:
  - loop: "{{ foo }}"
    debug:
      msg: "{{ dict(item.keys()|map('lower')|zip(item.values())) }}"
    register: r
  - debug:
      var: r.results|map(attribute="msg")|list
  • loopにて、listの各要素に対する処理に分解
  • item.keys()
    • dictのkey部分のlistを作成
    • 例えば["KEY1", "KEY2", "KEY3"]
    • item|dict2items|map(attribute='key')|listと同義
  • item.values()
    • dictのvalue部分のlistを作成
    • 例えば["Val1", "Val2", "Val3"]
    • item|dict2items|map(attribute='value')|listと同義
  • |map('lower')
    • listの各要素にlowerフィルターを適用
    • 例えば["key1", "key2", "key3"]を作成
    • 内部で指定するフィルターに引数が必要な場合、map('フィルター名', '第一引数', '第二引数',,)などとして渡す事が可能
    • 要素が文字列ならば、regex_replace フィルターを使用することで、map('regex_replace', '^(.*)$', 'https://\1')などとして、各文字列に対する正規表現での編集も可能
  • dict(xxxx|zip(yyyy))により、list xxxxとlist yyyyからdictを再作成
  • |map(attribute="msg")|list
    • debugのloop結果から出力内容"msg"の内容でlistを再作成

実行結果

 $ ansible-playbook -i localhost, site.yml

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

TASK [debug] *****************************************************************************************************
ok: [localhost] => (item={'KEY1': 'Val1', 'KEY2': 'Val2', 'KEY3': 'Val3'}) => {
    "msg": {
        "key1": "Val1",
        "key2": "Val2",
        "key3": "Val3"
    }
}
ok: [localhost] => (item={'KEY1': 'Val4', 'KEY2': 'Val5', 'KEY3': 'val6'}) => {
    "msg": {
        "key1": "Val4",
        "key2": "Val5",
        "key3": "Val6"
    }
}

TASK [debug] *****************************************************************************************************
ok: [localhost] => {
    "r.results|map(attribute=\"msg\")|list": [
        {
            "key1": "Val1",
            "key2": "Val2",
            "key3": "Val3"
        },
        {
            "key1": "Val4",
            "key2": "Val5",
            "key3": "Val6"
        }
    ]
}

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

参考

Ansible Tips: dictionary や list_of_dictionaries 構造に対する値の参照とループ操作
https://qiita.com/hiroyuki_onodera/items/b42f728c2270a7bccd97

雑感

使える武器がフィルターだったりpythonの関数だったり。。

別解1 loop時に作った解をまとめる


  - set_fact:
      list0: []
  - loop: "{{ foo }}"
    set_fact:
      list0: "{{ list0 + [ dict(item.keys()|map('lower')|zip(item.values())) ] }}"

  - debug:
      var: list0
TASK [set_fact] *****************************************************************************************************
ok: [localhost]

TASK [set_fact] *****************************************************************************************************
ok: [localhost] => (item={'KEY1': 'Val1', 'KEY2': 'Val2', 'KEY3': 'Val3'})
ok: [localhost] => (item={'KEY1': 'Val4', 'KEY2': 'Val5', 'KEY3': 'Val6'})

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "list0": [
        {
            "key1": "Val1",
            "key2": "Val2",
            "key3": "Val3"
        },
        {
            "key1": "Val4",
            "key2": "Val5",
            "key3": "Val6"
        }
    ]
}

別解2 jinja2のforを使用

可読性は最も高そう


  - loop: "{{ foo }}"
    debug: 
      msg: "{{ bar|from_yaml }}"
    vars:
      bar: >-
        {
          {% for k,v in item.items() %} 
            '{{k|lower}}': '{{v}}', 
          {% endfor %} 
        }
    register: r
  
  - debug:
      var: r.results|map(attribute="msg")|list
  • task varsにて{% %}によるforループを回して文字列のYAMLを作成、モジュール属性側でfrom_yamlフィルターを適用してYAMLとして認識
    • from_yamlフィルターの為の{{ }}内にforの為の{% %}を含めることができなかった為
  • forループは以下でも可
    • bar: "{ {% for i in foo|dict2items %} '{{i.key|lower}}': '{{i.value}}', {% endfor %} }"
TASK [debug] ********************************************************************************************************
ok: [localhost] => (item={'KEY1': 'Val1', 'KEY2': 'Val2', 'KEY3': 'Val3'}) => {
    "msg": {
        "key1": "Val1",
        "key2": "Val2",
        "key3": "Val3"
    }
}
ok: [localhost] => (item={'KEY1': 'Val4', 'KEY2': 'Val5', 'KEY3': 'Val6'}) => {
    "msg": {
        "key1": "Val4",
        "key2": "Val5",
        "key3": "Val6"
    }
}

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "r.results|map(attribute=\"msg\")|list": [
        {
            "key1": "Val1",
            "key2": "Val2",
            "key3": "Val3"
        },
        {
            "key1": "Val4",
            "key2": "Val5",
            "key3": "Val6"
        }
    ]
}
3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?