'How to compare more than two lists have the same values in same indexes in Ansible?

I would like to compare more than 2 lists, and see if they have the same values in same indexes.

I have the previous questions regarding how I can alternate the values in the multiple lists. here Based on this, I need to compare each values and stop if there is value non-identical. (There might be better solution, please welcome to add)

---
- name: data test
  hosts: localhost
  vars:
    data_1: [True, False, True]
    data_2: [False, True, True]
    
  tasks:
  - name: combine
    set_fact:
      comp_data: "{{ comp_data | default([]) + [item] }}"
    loop: 
      - "{{ data_1 }}"
      - "{{ data_2 }}"

  - name: list all comp_data
    debug:
      msg: "{{ comp_data }}"

  - name: zip
    set_fact:
      zip_data: "{{ lookup('together', *comp_data) }}"

  - name: list all zip comp_data
    debug:
      msg: "{{ zip_data }}"
    
  - name: check each element are identical
    assert:
      that:
        - "{{ item | unique | length == 1 }}"
      msg: |
        "Data is not identical"
        "{{ item }}"
    loop: "{{ zip_data }}"

Output:

TASK [check each element are identical] *************************************************************************************************************************************************
failed: [localhost] (item=[True, False]) => {
    "ansible_loop_var": "item",
    "assertion": false,
    "changed": false,
    "evaluated_to": false,
    "item": [
        true,
        false
    ]
}

MSG:

"Data is not identical"
"[True, False]"

failed: [localhost] (item=[False, True]) => {
    "ansible_loop_var": "item",
    "assertion": false,
    "changed": false,
    "evaluated_to": false,
    "item": [
        false,
        true
    ]
}

MSG:

"Data is not identical"
"[False, True]"

ok: [localhost] => (item=[True, True]) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": [
        true,
        true
    ]
}

MSG:

All assertions passed

At the end, it should stop as soon as it sees non-identical, but it iterates until the last list.

Any idea how I can stop right away and better way to compare lists if any?

Thank you,



Solution 1:[1]

Q: "Stop as soon as it sees non-identical items."

A: Find the index and slice the lists. For example, given the data

data_1: [True, False, True]
data_2: [False, True, True]

Find the index where the lists start to differ

data_diff: "{{ data_1|zip(data_2)|
               map('unique')|
               map('length')|
               map('log', 100)|map('round', 0, method='ceil')|map('int')|
               list }}"
data_stop_index: |-
  {% if 1 in data_diff %}{{ data_diff.index(1) }}
  {% else %}{{ data_diff|length }}
  {% endif %}

gives

data_diff: [1, 1, 0]
data_stop_index: 0

Then, the iteration

    - debug:
        msg: "{{ item|to_yaml }}"
      with_together:
        - "{{ data_1[:data_stop_index|int] }}"
        - "{{ data_2[:data_stop_index|int] }}"

gives no result because the first items are different


When you change the data

    data_1: [True, False, True]
    data_2: [True, True, True]

the above task will display the first items

  msg: |-
    [true, true]

The code can be modified to process more lists. For example, given the data

data_1: [A, B, C]
data_2: [A, X, C]
data_3: [A, B, C]

zip and flatten up to 100 lists (increase the logarithm base if you want more)

data_diff: "{{ data_1|zip(data_2)|zip(data_3)|
               map('flatten')|
               map('unique')|
               map('length')|
               map('log', 100)|map('round', 0, method='ceil')|map('int')|
               list }}"
data_stop_index: |-
  {% if 1 in data_diff %}{{ data_diff.index(1) }}
  {% else %}{{ data_diff|length }}
  {% endif %}

gives

data_diff: [0, 1, 0]
data_stop_index: 1

Then, slice and iterate together all lists

    - debug:
        msg: "{{ item|to_yaml }}"
      with_together:
        - "{{ data_1[:data_stop_index|int] }}"
        - "{{ data_2[:data_stop_index|int] }}"
        - "{{ data_3[:data_stop_index|int] }}"

gives the first items

  msg: |-
    [A, A, A]

Optionally, create a custom filter to compare the items in a list

shell> cat filter_plugins/bool.py 
def bool_gt(a, b):
    return (a > b)


class FilterModule(object):
    ''' Ansible filters for operating on Boolean '''

    def filters(self):
        return {
            'bool_gt': bool_gt,
        }

Then, use it instead of the awkward built-in filters' pipe (log, round, int)

data_diff: "{{ data_1|zip(data_2)|zip(data_3)|
               map('flatten')|
               map('unique')|
               map('length')|
               map('bool_gt', 1)|
               list }}"

and fit the calculation of the index

data_stop_index: |-
  {% if true in data_diff %}
  {{ data_diff.index(true) }}
  {% else %}
  {{ data_diff|length }}
  {% endif %}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1