Ansible Tips: dictionary や list_of_dictionaries 構造などにおいて使用できるフィルターなど

備忘


#dictionary や list_of_dictionaries 構造などにおいて使用できるフィルターなど


```
- hosts: all
  gather_facts: false
  tasks:

# list -> 全要素の数
  # |length

  - debug: { msg: "{{ [ 0, 1, 2, ]|length }}" }                   # "3" 


# list -> 特定の要素の数
  # .count()

  - debug: { msg: "{{ ['a', 'b', 'c', 'b'] .count('b') }}" }      # "2"      listにおける要素の数


# list -> 要素の場所(最初)
  # .index()

  - debug: { msg: "{{ ['a', 'b', 'c', 'b'] .index('b') }}" }      # "1"      listにおけるその値の要素の最初の場所。0始まり。


# list -> 特定の場所の要素
  # []
  # |first
  # |last

  - debug: { msg: "{{ ['a', 'b', 'c', 'd'][1] }}" }               # "b"      listにおけるその場所(0始まり)の値
  - debug: { msg: "{{ ['a', 'b', 'c', 'd'][-2] }}" }              # "c"      マイナスの場合は末尾から(-1始まり)
  - debug: { msg: "{{ ['a', 'b', 'c', 'd']|first }}" }            # "a"      [0]と同じ
  - debug: { msg: "{{ ['a', 'b', 'c', 'd']|last }}" }             # "d"      [-1]と同じ


# list -> 逆順のlist
  # |reverse

  - debug: { msg: "{{ ['a', 'b']|reverse }}" }                    # ['b', 'a']    listにおける要素を逆順に


# list -> 値が指定条件を満たすlistを抽出
  # |select()

  - debug: { msg: "{{ ['a', 'b', 'c'] | select('==','b') }}" }    # ["b"]
  - debug: { msg: "{{ ['a', 'b', 'b'] | select('==','b') }}" }    # ["b", "b"]
  - debug: { msg: "{{ ['a', 'b', 'c'] | select('==','d') }}" }    # []
  - debug: { msg: "{{ ['c', 'b', 'a'] | select('<=','b') }}" }    # ["b", "a"]
  - debug: { msg: "{{ ['c', 'b', 'a'] | select('>=','b') }}" }    # ["c", "b"]
  - debug: { msg: "{{ ['c', 'b', 'a'] | select('<','b') }}" }     # ["a"]
  - debug: { msg: "{{ ['c', 'b', 'a'] | select('>','b') }}" }     # ["c"]
  - debug: { msg: "{{ ['a', '(b)', 'c'] | select('match', '\\(.*\\)') | list }}" } # ["(b)"]

        # https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.select

        # 使えるテストは以下という理解だが、matchが見当たらない^^;
        # https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-tests

        # Ansibleにより提供されたフィルターか
        # https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_tests.html#testing-strings


# list -> 各要素にfilterを適用したlistを作成
  # |map()

  - debug: { msg: "{{ ['a', 'b', 'c'] | map('upper') }}" }       # [ "A", "B", "C" ]  # filterへの引数はmap内で","区切りで指定
 
        # https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.map

# listと値 -> (listの要素をkeyとする)dictを作成
  # .fromkeys()

  - debug: { msg: "{{ {}.fromkeys(['a','b'],0) }}" }              # { "a": 0, "b": 0 }                listの要素をキーとするdictを作成
  - debug: { msg: "{{ {}.fromkeys(['a','b'],[7,8]) }}" }          # { "a": [ 7, 8 ], "b": [ 7, 8 ] }  listの要素をキーとするdictを作成

# list -> dict
  # items2dict
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#transforming-lists-into-dictionaries
# dict -> 特定のkeyのvalueを抽出
  # []
  # .KEY
  # .get()

  - debug: { msg: "{{ {'k0': 'v0', 'k1': 'v1'}['k1'] }}" }        # "v1"
  - debug: { msg: "{{ {'k0': 'v0', 'k1': 'v1'}.k1 }}" }           # "v1"  注: 指定キーが予約語(例えばindexなど)と重複する場合、この書式は使用できない
  - debug: { msg: "{{ {'k0': 'v0', 'k1': 'v1'}.get('k1') }}" }    # "v1"



# list_of_dict -> 特定のkeyに対応するvalueを抜き出してlistを作成
  # |map(attribute=KEY)

  - debug: { msg: "{{ [{'k1': 'v11', 'k2': 'v12'}, {'k1': 'v21', 'k2': 'v22'}]|map(attribute='k2')|list }}" }       # [ "v12", "v22" ] なお、"|list"は新しいansibleでは不要かも


# list_of_dict -> 指定したkeyに関してグルーピングしたlistを作成
  # |groupby()

  - debug:
      msg: "{{ address_book|groupby('addr') }}"
    vars:
      address_book:
      - {name: foo, addr: tok}
      - {name: bar, addr: tok}
      - {name: baz, addr: chiba}
    register: r

        # 結果
        # - - chiba
        #   - - {name: baz, addr: chiba}
        # - - tok
        #   - - {name: foo, addr: tok}
        #     - {name: bar, addr: tok}


# list_of_dict -> 特定の要件を満たす部分集合
  # |selectattr(key,test,arg)
  # |rejectattr(key,test,arg)

  # testには、==などの他にinやcontainsといったものも使用可能
# https://zaki-hmkc.hatenablog.com/entry/2021/02/18/000228



# dict ->  keysのlistを作成
  # .keys()

  - debug: { msg: "{{ {'k0': 'v0', 'k1': 'v1'}.keys() }}" }       # ["k0", "k1"]


# dict ->  valesのlistを作成
  # .values()

  - debug: { msg: "{{ {'k0': 'v0', 'k1': 'v1'}.values() }}" }     # ["v0", "v1"]


# dict -> [キー, 値]のlistに変換
  # .items()

  - debug: { msg: "{{ {'k0': 'v0', 'k1': 'v1'}.items() }}" }      # [["k0", "v0"], ["k1", "v1"]]
  - debug: { msg: "{{ {'k1': 'v1', 'k0': 'v0'}.items() }}" }      # [["k1", "v1"], ["k0", "v0"]] # 順序が考慮されていて上の行と同じ結果でない。バージョンなどにより結果が異る可能性。


# dict -> key:,value:書式dictのlistを作成 (loop:の引数などに利用可能)
  # |dict2items
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#transforming-dictionaries-into-lists

    #   k0: v0
    #   k1: v1
    #   ↓
    #   - key: k0
    #     value: v0
    #   - key: k1
    #     value: v1

  - debug: { msg: "{{ {'k0': 'v0', 'k1': 'v1'}|dict2items|list }}" } # [{"key": "k0", "value": "v0"}, {"key": "k1", "value": "v1"}] なお、"|list"は新しいansibleでは不要かも

# key,valueのlistのlistをdictに変換
  # dict(...)
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#combining-items-from-multiple-lists-zip-and-zip-longest

    # - - k0
    #   - v0
    # - - k1
    #   - v1
    # ↓
    # k0: v0
    # k1: v1

  - debug: { var: dict( [['k1','v1'], ['k2','v2']] ) }    # => { "k1": "v1", "k2": "v2" }


# ある要素がキー->下位listとなっているlist -> そのキー以下のlistを用いた疑似2重ループ用のデータに変換
  # |subelements(キー)  
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#combining-objects-and-subelements

  - debug:
      msg: "{{ users|subelements('groups') }}"
    vars:
      users:
        - name: user1
          groups: [wheel, hoge]
        - name: user2
          groups: [hoge]

        # 結果
        # - - name: user1
        #     groups: [wheel, hoge]
        #   - wheel
        # - - name: user1
        #     groups: [wheel, hoge]
        #   - hoge
        # - - name: user2
        #     groups: [hoge]
        #   - hoge


# dict -> changedキーやfailedキーなどに対する値(true/false)
  # is changed
  # ['changed']
  # .changed

  - debug: { msg: "{{ {'changed': true } is changed }}" }         # true
  - debug: { msg: "{{ {'changed': false} is changed }}" }         # false
  - debug: { msg: "{{ {                } is changed }}" }         # false  'changed'キーがなくともエラーにならない
  - debug: { msg: "{{ {                } is not changed }}" }     # true   'changed'キーがなくともエラーにならない
  - debug: { msg: "{{ {'changed': true }['changed'] }}" }         # true
  - debug: { msg: "{{ {'changed': true }.changed }}" }            # true
# - debug: { msg: "{{ {                }.changed }}" }            # 'dict object' has no attribute 'changed'


# 要素1が集合2に含まれるかどうか、文字列string1がstring2 に含まれるかどうか
  # 要素 in 集合
  # string1 in string2
  # string1.find(string2)

  - debug: { msg: "{{ 'a' in ['a', 'b', 'c'] }}" }                # true
  - debug: { msg: "{{ 'z' in ['a', 'b', 'c'] }}" }                # false
# - debug: { msg: "{{ ['a', 'b', 'c'].find('a') }}" }             # 'list object' has no attribute 'find'

  - debug: { msg: "{{ 'ab' in 'abcd' }}" }                        # true
  - debug: { msg: "{{ 'ba' in 'abcd' }}" }                        # false
  - debug: { msg: "{{ 'ab' not in 'abcd' }}" }                    # false
  - debug: { msg: "{{ 'abcd'.find('ab') }}" }                     # 0
  - debug: { msg: "{{ 'abcd'.find('ba') }}" }                     # -1

        # https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-tests.in


# 型の表示

  - debug: { msg: "{{ 1|type_debug }}" }                          # "int"
  - debug: { msg: "{{ []|type_debug }}" }                         # "list"
  - debug: { msg: "{{ {}|type_debug }}" }                         # "dict"
  - debug: { msg: "{{ true|type_debug }}" }                       # "bool"
  - debug: { msg: "{{ 'abc'|type_debug }}" }                      # "str"
  - debug: { msg: "{{ 'あいうえお'|type_debug }}" }                 # "str"
  - debug: { msg: "{{ foo|type_debug }}" }                        # "int"
    vars: { foo: 1 }
  - debug: { msg: "{{ foo|type_debug }}" }                        # "int"
    vars: { foo: "{{ bar }}", bar: 2 }
  - debug: { msg: "{{ foo|type_debug }}" }                        # "AnsibleUnicode"
    vars: { foo: abc }


# 構造化データ -> string
  # |to_nice_yaml  # ブロックスタイルYAML書式の文字列に変換
  # |to_yaml       # 下層にフロースタイルを混ぜてより短く表示。個人的にはto_nice_yamlより改行が少く見通しがかなり良い。


# string -> 構造化データ
  # |from_yaml     # YAML形式の文字列を解釈
  # |from_json     # JSON形式の文字列を解釈


# 一般的と思われるもの

# == もしくは eq
# != もしくは ne 
# >  もしくは gt
# <  もしくは lt
# >= もしくは ge
# <= もしくは le

# https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-filters

# |basename
# |dirname

# |int
# |float
# |bool
# |string

# |sort()

# |join()

# |lower
# |upper

# |b64decode

# |max
# |min
# |sum


# 以下未完

# dict -> キーで並び換え  本来dictsは順序を持たず、実際Pythonでは順序は保証されないが、for loopなどで順序を使用したい場合に使用。
  # |dictsort  

# 複数の構造化データ -> 単一の構造化データ
  # h1|combine(h2,h3,..)  
  # [h1,h2,h3,...]|combine  
# 複数の構造化データ -> 単一の構造化データ  Deepマージ
  # h1|combine(h2,h3,.. ,recursive=true)  
  # [h1,h2,h3,...]|combine(recursive=true)  
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#combining-hashes-dictionaries

# 構造化データをより高度に扱う
  # |json_query()

# 変数が未定義の場合のデフォルト値
  # |default(値) 
  # |default(omit)   # 特殊: 未定義の場合にその設定そのものを取り止め
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#providing-default-values
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#making-variables-optional

# string -> 正規表現による置き換え
  # |regex_replace('^.* = +(.*)$','\\1') 
  # |regex_replace('.yml')

# 多重階層のデータをフラット化
  # |flatten(levels=0)  

# プラグイン
  # https://docs.ansible.com/ansible/2.9_ja/plugins/lookup/pipe.html

  # lookup('env', 'PWD')  
  # lookup('pipe', 'date')  


# https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html

# 3項演算
  # | ternary(x,y[,z]) # zは値がnullの場合
  # x if 式 else y
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#defining-different-values-for-true-false-null-ternary

# set もしくは list
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#selecting-from-sets-or-lists-set-theory

# listから重複削除
  # uniq
# 和集合
  # union
# 積集合
  # intersect
# 差集合
  # | difference
# 排他的論理和
  # | symmetric_difference


# 複数のlistから要素を1：1で抜き出したもののlistを作成
  # | zip()|list
  # | zip_longest()|list
  # https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#combining-items-from-multiple-lists-zip-and-zip-longest


# 2要素listのlistからdictを作成
  # dict()

- debug: { var: dict([['k1','v1'],['k2','v2']]) }  # => {"k1": "v1", "k2": "v2"}
```


# 参考

https://zaki-hmkc.hatenablog.com/entry/2021/02/18/000228