Search code examples
listdictionaryansiblejson-query

Ansible - Identify Key in dictionary when Key values are lists of dicts


I have the following dictionary

  grouped:
    10000:
    - {Id: 10001, Name: North_America, Parent_Id: 10000, Type: Country}
    10001:
    - {Id: 10011, Name: Maine, Parent_Id: 10001, Type: State}
    - {Id: 10012, Name: Colorado, Parent_Id: 10001, Type: State}
    - {Id: 10013, Name: Texas, Parent_Id: 10001, Type: State}
    10011:
    - {Id: 10101, Name: Augusta, Parent_Id: 10011, Type: City}
    - {Id: 10102, Name: Portland, Parent_Id: 10011, Type: City}
    10012:
    - {Id: 10103, Name: Denver, Parent_Id: 10012, Type: City}
    10013:
    - {Id: 10104, Name: Austin, Parent_Id: 10013, Type: City}
    - {Id: 10105, Name: Houston, Parent_Id: 10013, Type: City}
    10101:
    - {Id: 11001, Name: First_st, Parent_Id: 10101, Type: Street}
    - {Id: 11002, Name: Second_st, Parent_Id: 10101, Type: Street}
    10102:
    - {Id: 11003, Name: First_st, Parent_Id: 10102, Type: Street}
    - {Id: 11004, Name: Second_st, Parent_Id: 10102, Type: Street}
    10104:
    - {Id: 11005, Name: First_st, Parent_Id: 10104, Type: Street}
    - {Id: 11006, Name: Second_st, Parent_Id: 10104, Type: Street}

If I'm given a string containing Country-State-City-Street, eg North_America-Texas-Austin-First_st, I want to

  1. Search the disc for North_America, if it exists collect it's ID.
  2. Using the ID of North_America, search the Key of the same value and check that it contains a value with name == Texas. If it exists, collect the ID of Texas
  3. Using the id of Texas, search the key of the same value and check that it contains a value with name == Austin
  4. Repeat for street...

The end goal, if the search string "North_America-Texas-Austin-Third_st" is provided and it does not exist in the dict, it will be added

First step is to identify the required Key, I've attempted json_queries and selectattr to do this but none work

---
  hosts: localhost
  gather_facts: false
  connection: local
  vars:
    target: "North_America-Colorado-Denver-First_st"

  tasks:
    - name: Get the id using json filter and jmespath query
      set_fact:
        group_id: "{{ grouped | json_query(ID_query) | map(attribute='key') }}"
      vars:
        ID_query: '[?[*].Name == `North-America`]'
    - debug: var=group_id

    - name: Get the id using selectattr
      set_fact:
        group_id2: "{{ grouped | dict2items | selectattr('Name', 'search', 'North_America') }}"
    - debug: var=group_id2

Output from the first debug is an empty list

ok: [localhost] => {
    "group_id": []
}

Output from the second debug says that the dict object has no attribute Name


Solution

  • Create a dictionary of ids

      ids: "{{ dict(grouped.keys()|
                    zip(grouped.values()|
                        map('items2dict', key_name='Name', value_name='Id'))) }}"
    

    gives

      ids:
        10000: {North_America: 10001}
        10001: {Colorado: 10012, Maine: 10011, Texas: 10013}
        10011: {Augusta: 10101, Portland: 10102}
        10012: {Denver: 10103}
        10013: {Austin: 10104, Houston: 10105}
        10101: {First_st: 11001, Second_st: 11002}
        10102: {First_st: 11003, Second_st: 11004}
        10104: {First_st: 11005, Second_st: 11006}
    

    Then, use it. For example

        - debug:
            msg: "{{ item }} [{{ target_ok }}] {{ target_id }}"
          loop:
            - North_America-Colorado-Denver-First_st
            - North_America-Texas-Austin-First_st
            - North_America-Texas-Austin-Third_st
          vars:
            f: "{{ item|split('-') }}"
            country: "{{ ids.10000[f.0]|d('undef') }}"
            state: "{{ ids[country|int][f.1]|d('undef') }}"
            city: "{{ ids[state|int][f.2]|d('undef') }}"
            street: "{{ ids[city|int][f.3]|d('undef') }}"
            target_id: "{{ country }} {{ state }} {{ city }} {{ street }}"
            target_ok: "{{ ('undef' in target_id.split())|ternary('KO', 'OK') }}"
    

    gives abridged

      msg: North_America-Colorado-Denver-First_st [KO] 10001 10012 10103 undef
      msg: North_America-Texas-Austin-First_st [OK] 10001 10013 10104 11005
      msg: North_America-Texas-Austin-Third_st [KO] 10001 10013 10104 undef