如何比较两个以上的列表在 Ansible 的相同索引中具有相同的值?

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

我想比较 2 个以上的列表,看看它们在相同索引中是否具有相同的值。

我之前有关于如何交替多个列表中的值的问题。 基于此,我需要比较每个值并在值不相同时停止。 (可能有更好的解决办法,欢迎补充)

---
- 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 }}"

输出:

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

最后,它应该一看到不相同就停止,但它会迭代到最后一个列表。

知道如何立即停止以及比较列表的更好方法吗?

谢谢,

问:“一看到 non-identical 项就停止。”

A:查找索引和 slice 列表。例如,给定数据

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

找到列表开始不同的索引

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 %}

给予

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

然后,迭代

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

没有给出结果,因为前几项不同


当您更改数据时

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

以上任务将显示第一个项目

  msg: |-
    [true, true]

可以修改代码以处理更多列表。例如,给定数据

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

zipflatten 最多 100 个列表(如果需要更多,请增加对数底数)

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 %}

给予

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

然后,对所有列表进行切片和迭代

    - 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] }}"

给出第一个项目

  msg: |-
    [A, A, A]

可以选择创建自定义过滤器来比较列表中的项目

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,
        }

然后,使用它代替笨拙的 built-in 过滤器管道 (log, round, int)

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

并拟合指数的计算

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