Search code examples
ansibleansible-factsansible-template

merge two lists based on a condition


    "ansible_facts": {
        "list1": [
            "10",
            "2",
            "4",
            "5",
            "6",
            "7",
            "7",
            "8",
            "9"
        ],
        "list2": [
            [
                "10",
                "xxxxx"
            ],
            [
                "2",
                "yyyyy"
            ],
            [
                "4",
                "zzzzz"
            ],
            [
                "5",
                "mmmmm"
            ],
            [
                "6",
                "fffff"
            ],
            [
                "7",
                "gggggg"
            ],
            [
                "8",
                "hhhhh"
            ],
            [
                "9",
                "kkkkkk"
            ]

Here I have two lists, I want to merge list2, just the words(xxxx,yyyy,...) with list1, but with condition that whenever the number on list2 match list1 then merge it to it's according number on list 1. But as you can see there can be some repeating number making it not perfectly aligned. When I sort the list like above still the the condition won't work when numbers repeat.

``
the condition that i've tried
    when: list1[item|int] == list2[item|int]
    with_sequence: start=0 end={{countvar.stdout|int - 1}}

Expected merged list
```

    "list3": [
        [
            "10",
            "xxxxx"
        ],
        [
            "2",
            "yyyyy"
        ],
        [
            "4",
            "zzzzz"
        ],
        [
            "5",
            "mmmmm"
        ],
        [
            "6",
            "fffff"
        ],
        [
            "7",
            "gggggg"
        ],
        [
            "7",
            "gggggg"
        ],
        [
            "8",
            "hhhhh"
        ],
        [
            "9",
            "kkkkkk"
        ]

Solution

  • Given the lists
      l1: ['10', '2', '4', '5', '6', '7', '7', '8', '9']
      l2:
        - ['10', xxxxx]
        - ['2', yyyyy]
        - ['4', zzzzz]
        - ['5', mmmmm]
        - ['6', fffff]
        - ['7', gggggg]
        - ['8', hhhhh]
        - ['9', kkkkkk]
    

    1. Convert the second list to a dictionary
      d2: "{{ dict(l2) }}"
    

    gives

      d2:
        '10': xxxxx
        '2': yyyyy
        '4': zzzzz
        '5': mmmmm
        '6': fffff
        '7': gggggg
        '8': hhhhh
        '9': kkkkkk
    
    1. Extract the values
      v1: "{{ l1|map('extract', d2)|list }}"
    

    gives

      v1: [xxxxx, yyyyy, zzzzz, mmmmm, fffff, gggggg, gggggg, hhhhh, kkkkkk]
    
    1. zip the lists
      l3: "{{ l1|zip(v1) }}"
    

    gives what you're looking for

      l3:
        - ['10', xxxxx]
        - ['2', yyyyy]
        - ['4', zzzzz]
        - ['5', mmmmm]
        - ['6', fffff]
        - ['7', gggggg]
        - ['7', gggggg]
        - ['8', hhhhh]
        - ['9', kkkkkk]
    

    You can put it in one line which gives the same result

      l3: "{{ l1|zip(l1|map('extract', dict(l2))|list) }}"
    

    • Example of a complete playbook for testing
    - hosts: localhost
    
      vars:
    
        l1: ['10', '2', '4', '5', '6', '7', '7', '8', '9']
        l2:
          - ['10', xxxxx]
          - ['2', yyyyy]
          - ['4', zzzzz]
          - ['5', mmmmm]
          - ['6', fffff]
          - ['7', gggggg]
          - ['8', hhhhh]
          - ['9', kkkkkk]
    
        d2: "{{ dict(l2) }}"
        v1: "{{ l1|map('extract', d2)|list }}"
        l3: "{{ l1|zip(v1) }}"
        l4: "{{ l1|zip(l1|map('extract', dict(l2))|list) }}"
    
      tasks:
    
        - debug:
            var: d2
        - debug:
            var: v1|to_yaml
        - debug:
            var: l3|to_yaml
        - debug:
            var: l4|to_yaml
    
    • The filter to_yaml can use anchor
     l3|to_yaml: |-
        - ['10', xxxxx]
        - ['2', yyyyy]
        - ['4', zzzzz]
        - ['5', mmmmm]
        - ['6', fffff]
        - &id001 ['7', gggggg]
        - *id001
        - ['8', hhhhh]
        - ['9', kkkkkk]
    
    shell> ansible-config dump | grep DEFAULT_STDOUT_CALLBACK
    DEFAULT_STDOUT_CALLBACK(/scratch/test-061/ansible.cfg) = yaml