5
0

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 1 year has passed since last update.

AnsibleAdvent Calendar 2023

Day 12

【Ansible】Ansibleで集合論フィルターを使いたい

Last updated at Posted at 2023-12-11

はじめに

 Ansibleで2つのリストを比較し、両方に含まれる要素やどちらかだけに含まれる要素を
抽出したいことがあると思います。その場合は、集合論フィルターが便利なので実際に使ってみました。

TL;DR

  • Ansibleでは以下の集合論フィルターが使用可能。リストだけでなく、辞書のリストに対しても使用可能。

    集合論フィルター 演算子 説明 記載例
    unique - 重複を排除 {{ list_A | unique }}
    intersect A ∩ B 積集合(AかつB) {{ list_A | intersect(list_B) }}
    union A ∪ B 和集合(AまたはB)
    ※AかつBも含まれる
    {{ list_A | union(list_B) }}
    symmetric_difference A xor B 排他的論理和(AのみまたはBのみ)
    ※AかつBは含まれない
    {{ list_A | symmetric_difference(list_B) }}
    difference A ー B 差集合(AにはあるがBにはない) {{ list_A | difference(list_B) }}
  • 辞書のリスト同士を比較した際に同じ要素とみなされるには、辞書内のすべてのキーと値が一致している必要がある。

  • 集合論フィルターを活用することで、辞書のリスト同士の比較が容易になる。

前提条件

  • Ansibleにおけるフィルターの使い方を理解していること

実行環境

  • Python: 3.9.16
  • Ansible: 2.9.7
  • Jinja2: 3.0.3

詳細

1. リストに対して集合論フィルターを使用

1-1. 実行するPlaybook

  • Playbookでは、以下2つのことを確認します
    • 変数list_uniqueに対し、リストの重複が排除されること(unique)
    • 変数list_Alist_Bに対し、正しい演算が行われること(intersect/union/symmetric_difference/difference)
list_set_theory_filters.yml
---
- name: "List set theory filters"
  hosts: localhost
  vars:
    list_unique: [1, 2, 3, 2, 3, 4]    # 2と3が重複
    list_A: [1, 2, 3, 4]   # 3と4が重複
    list_B: [3, 4, 5, 6]
  gather_facts: false
  tasks:
    - name: Test unique
      debug:
        msg: "{{ list_unique | unique }}"

    - name: Test intersect
      debug:
        msg: "{{ list_A | intersect(list_B) }}"

    - name: Test union
      debug:
        msg: "{{ list_A | union(list_B) }}"

    - name: Test symmetric_difference
      debug:
        msg: "{{ list_A | symmetric_difference(list_B) }}"

    - name: Test difference
      debug:
        msg: "{{ list_A | difference(list_B) }}"

1-2. 実行結果

想定通りの結果を得ることができました。

$ ansible-playbook list_set_theory_filters.yml
PLAY [List set theory filters] *******************************************************************

TASK [Test unique] *******************************************************************************
ok: [localhost] =>
  msg:
  - 1
  - 2
  - 3
  - 4

TASK [Test intersect] ****************************************************************************
ok: [localhost] =>
  msg:
  - 3
  - 4

TASK [Test union] ********************************************************************************
ok: [localhost] =>
  msg:
  - 1
  - 2
  - 3
  - 4
  - 5
  - 6

TASK [Test symmetric_difference] *****************************************************************
ok: [localhost] =>
  msg:
  - 1
  - 2
  - 5
  - 6

TASK [Test difference] ***************************************************************************
ok: [localhost] =>
  msg:
  - 1
  - 2

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

2. 辞書のリストに対して集合論フィルターを使用

2-1. 実行するPlaybook

  • Playbookでは1つ前と同様に、以下2つのことを確認します
    • 変数list_uniqueに対し、リストの重複が排除されること(unique)
    • 変数list_Alist_Bに対し、正しい演算が行われること(intersect/union/symmetric_difference/difference)
dict_set_theory_filters.yml
---
- name: "Dict set theory filters"
  hosts: localhost
  vars:
    list_unique:
      - {id: 1, name: "test 1"}
      - {id: 2, name: "test 2"}
      - {id: 3, name: "test 3"}
      - {id: 2, name: "test 2"}
      - {id: 3, name: "test 3"}
      - {id: 4, name: "test 4"}
    list_A:
      - {id: 1, name: "test 1"}
      - {id: 2, name: "test 2"}
      - {id: 3, name: "test 3"}
      - {id: 4, name: "test 4"}
    list_B:
      - {id: 3, name: "test 3"}
      - {id: 4, name: "test 4"}
      - {id: 5, name: "test 5"}
      - {id: 6, name: "test 6"}
  gather_facts: false
  tasks:
    - name: Test unique
      debug:
        msg: "{{ list_unique | unique }}"

    - name: Test intersect
      debug:
        msg: "{{ list_A | intersect(list_B) }}"

    - name: Test union
      debug:
        msg: "{{ list_A | union(list_B) }}"

    - name: Test symmetric_difference
      debug:
        msg: "{{ list_A | symmetric_difference(list_B) }}"

    - name: Test difference
      debug:
        msg: "{{ list_A | difference(list_B) }}"

2-2. 実行結果

辞書のリストに対しても、想定通りの結果を得ることができました。

$ ansible-playbook dict_set_theory_filters.yml
PLAY [Dict set theory filters] **********************************************************

TASK [Test unique] **********************************************************************
ok: [localhost] =>
  msg:
  - id: 1
    name: test 1
  - id: 2
    name: test 2
  - id: 3
    name: test 3
  - id: 4
    name: test 4

TASK [Test intersect] *******************************************************************
ok: [localhost] =>
  msg:
  - id: 3
    name: test 3
  - id: 4
    name: test 4

TASK [Test union] ***********************************************************************
ok: [localhost] =>
  msg:
  - id: 1
    name: test 1
  - id: 2
    name: test 2
  - id: 3
    name: test 3
  - id: 4
    name: test 4
  - id: 5
    name: test 5
  - id: 6
    name: test 6

TASK [Test symmetric_difference] ********************************************************
ok: [localhost] =>
  msg:
  - id: 1
    name: test 1
  - id: 2
    name: test 2
  - id: 5
    name: test 5
  - id: 6
    name: test 6

TASK [Test difference] ******************************************************************
ok: [localhost] =>
  msg:
  - id: 1
    name: test 1
  - id: 2
    name: test 2

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

2-3. 注意点

  • 辞書のリスト同士を比較した際に同じ要素とみなされるには、辞書内のすべてのキーと値が一致している必要があります。
    • 例1)intersectフィルターでidnameの値は一致しているが、list_Bにのみキーhogeがあるので同じとみなされない

      • タスク

        - name: Test intersect
          debug:
            msg: "{{ list_A | intersect(list_B) }}"
          vars:
            list_A:
              - {id: 1, name: "test 1"}
            list_B:
              - {id: 1, name: "test 1", hoge: "hoge"}
        
      • 実行結果

        TASK [Test intersect] ***************************************************************
        ok: [localhost] =>
          msg: []
        
    • 例2)intersectフィルターで両方にキーidnameはあるが、キーnameの値が異なるので同じとみなされない

      • タスク

        - name: Test intersect
          debug:
            msg: "{{ list_A | intersect(list_B) }}"
          vars:
            list_A:
              - {id: 1, name: "test 1"}
            list_B:
              - {id: 1, name: "hoge"}
        
      • 結果

        TASK [Test intersect] ***************************************************************
        ok: [localhost] =>
          msg: []
        

3. 実践例

集合論フィルターを活用することで、辞書のリスト同士の比較が容易になります。

3-1. list_Aとlist_Bに重複がないことの確認

list_Alist_Bに重複がないことを確認する場合の例が以下です。例では、list_Alist_Bの両方に1があるのでエラーになります。

  • タスク

    - name: Check that there are no duplicates in list_A and list_B
      assert:
        that:
          -  (list_A | intersect(list_B)) | length == 0
        fail_msg:
          - "list_A and list_B: {{ list_A | intersect(list_B) }}"
          - "Only list_A: {{ list_A | difference(list_B) }}"
          - "Only list_B: {{ list_B | difference(list_A) }}"
      vars:
        list_A: [1]
        list_B: [1, 2]
    
  • 実行結果

    TASK [Check that there are no duplicates in list_A and list_B] **********************
    fatal: [localhost]: FAILED! => changed=false
      assertion: (list_A | intersect(list_B)) | length == 0
      evaluated_to: false
      msg:
      - 'list_A and list_B: [1]'
      - 'Only list_A: []'
      - 'Only list_B: [2]'
    

3-2. list_Aがlist_Bに含まれることの確認(A⊂B)

list_Alist_Bに含まれることを確認する場合の例が以下です。例では、list_A5がに含まれているのでエラーになります。

  • タスク

    - name: Check that list_A is in list_B
      assert:
        that:
          -  (list_A | intersect(list_B)) == list_A
        fail_msg:
          - "list_A and list_B: {{ list_A | intersect(list_B) }}"
          - "Only list_A: {{ list_A | difference(list_B) }}"
          - "Only list_B: {{ list_B | difference(list_A) }}"
      vars:
        list_A: [1, 2, 5]
        list_B: [1, 2, 3, 4]
    
  • 実行結果

    TASK [Check that list_A is in list_B] ***********************************
    fatal: [localhost]: FAILED! => changed=false
      assertion: (list_A | intersect(list_B)) == list_A
      evaluated_to: false
      msg:
      - 'list_A and list_B: [1, 2]'
      - 'Only list_A: [5]'
      - 'Only list_B: [3, 4]'
    

さいごに

  • 集合論フィルターを活用することで、辞書のリスト同士の比較が容易になるので積極的に活用しましょう。
  • 集合論フィルターの使い方を整理しながら、高校数学の「集合と論理」を思い出しました。懐かしいですね。

参考URL


5
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
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?