Merging two list of dictionaries according to a key value in Ansible

Viewed 477

I have two list of dictionaries. I want to merge them according to a specific key.

Here is list1:

"list1": [
  { "a": "b", "c": "d" },
  { "a": "e", "c": "f" }
]

Here is list2:

"list2": [
  { "a": "e", "g": "h" },
  { "a": "b", "g": "i" }
]

So after merging the final list of dictionaries should look like below. The merging should be done according the value of key "a":

"list3": [
  { "a": "b", "c": "d", "g": "i" },
  { "a": "e", "c": "f", "g": "h" }
]

Official documentation seems not enough for loops, iteration etc. I couldn't find a way to accomplish this task.

1 Answers

The simple option is to reverse the second list and combine the zip of the lists

  list3: "{{ list1|zip(list2|reverse)|map('combine')|list }}"

gives

  list3:
    - {a: b, c: d, g: i}
    - {a: e, c: f, g: h}

The next option is to iterate the first list. In the loop select the items from the second list and combine them. The expression below gives the same result

    - set_fact:
        list3: "{{ list3|d([]) + [item|combine(_selection)] }}"
      loop: "{{ list1 }}"
      vars:
        _selection: "{{ list2|selectattr('a', '==', item.a)|combine }}"

You can use json_query instead of selectattr if you want to

      vars:
        _selection: "{{ list2|json_query(_query)|combine }}"
        _query: '[?a==`{{ item.a }}`]'

If you can install the collection community.general use the filter lists_mergeby. The expression below gives the same result

  list3: "{{ list1|lists_mergeby(list2, 'a') }}"

If you're not able to install the collection create a custom filter on your own. For example

shell> cat filter_plugins/my_lists_mergeby.py
from collections import defaultdict
from operator import itemgetter


def my_lists_mergeby(l1, l2, index):

    d = defaultdict(dict)
    for l in (l1, l2):
        for elem in l:
            if index in elem.keys():
                d[elem[index]].update(elem)
    return sorted(d.values(), key=itemgetter(index))


class FilterModule(object):

    def filters(self):
        return {
            'my_lists_mergeby': my_lists_mergeby,
        }
Related