'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 |