You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ansible如何基于字典属性对字典列表进行嵌套重组

问题描述

我有一个包含Country/State/City/Street信息的字典列表,需要通过Parent_Id属性重组为层级嵌套结构,支持后续在指定City下搜索Street是否存在,不存在则新增。

原始数据结构

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

预期嵌套结构

Country
State_1
City_1
Street_1
Street_2
City_2
Street_1
Street_2
State_2
City_1
Street_1
Street_2
City_2
Street_1
Street_2

尝试的Playbook及问题

我尝试用groupby操作构建结构,但未得到预期结果,多次分组还报错。比如执行grouped: "{{ map.addresses | groupby('Parent_Id') | groupby('Id') }}"时,提示第二个元素不存在。以下是我写的Playbook:

---
- name: Use groupby to create needed structure
  hosts: localhost
  gather_facts: false
  connection: local

  tasks:
    - include_vars:
        file: ../map.json
        name: map
    
    - name: set fact to group
      set_fact:
        grouped: "{{ map.addresses | groupby('Parent_Id') }}"
    
    - name: print grouped
      debug:
        var: grouped

    - name: set fact to re group
      set_fact:
        regrouped: "{{ regrouped | d([]) + [{ 'type': item.1 | map(attribute='Type')|list,
                                              'name': item.1 | map(attribute='Name')|list|flatten }] }}"
      loop: "{{ grouped }}"
    
    - name: print regrouped
      debug:
        var: regrouped
解决方案

问题根源

直接用groupby('Parent_Id')只能得到按父ID分组的扁平结构,无法自动构建层级嵌套。后续二次groupby('Id')报错是因为groupby('Parent_Id')返回的是元组列表((父ID, 子元素列表)),元组没有Id属性,自然无法分组。

正确实现方式

我们需要先将所有地址项按Id建立索引,然后从顶层(Country)开始迭代构建嵌套结构。以下是可实现需求的Playbook:

---
- name: Build nested address structure
  hosts: localhost
  gather_facts: false
  connection: local

  tasks:
    - name: Load address data
      include_vars:
        file: ../map.json
        name: map

    - name: Create address index by Id
      set_fact:
        address_index: "{{ address_index | default({}) | combine({ item.Id: item }) }}"
      loop: "{{ map.addresses }}"

    - name: Build nested structure starting from Country
      set_fact:
        nested_addresses: "{{ nested_addresses | default([]) + [_build_nested(item)] }}"
      loop: "{{ map.addresses | selectattr('Type', 'equalto', 'Country') | list }}"
      vars:
        _build_nested: >-
          {%- set node = address_index[item.Id] -%}
          {%- set children = map.addresses | selectattr('Parent_Id', 'equalto', node.Id) | list -%}
          {
            "Name": "{{ node.Name }}",
            "Type": "{{ node.Type }}",
            "Id": "{{ node.Id }}",
            "Children": [
              {%- for child in children -%}
                {{ _build_nested(child) }}{% if not loop.last %},{% endif %}
              {%- endfor -%}
            ]
          }

    - name: Print nested structure
      debug:
        var: nested_addresses

    - name: Example: Check if Street exists in Augusta and add if not
      set_fact:
        nested_addresses: "{{ _update_street(nested_addresses, 'Augusta', 'Third_st') }}"
      vars:
        _update_street: >-
          {%- macro find_city(nodes) -%}
            {%- for node in nodes -%}
              {%- if node.Type == 'City' and node.Name == city_name -%}
                {{ node }}
              {%- elif node.Children | length > 0 -%}
                {%- set found = find_city(node.Children) -%}
                {%- if found -%}{{ found }}{%- endif -%}
              {%- endif -%}
            {%- endfor -%}
          {%- endmacro -%}
          {%- set city = find_city(nodes) -%}
          {%- if city -%}
            {%- set street_exists = city.Children | selectattr('Name', 'equalto', street_name) | list | length > 0 -%}
            {%- if not street_exists -%}
              {%- set new_street = {
                "Name": street_name,
                "Type": "Street",
                "Id": (11000 + city.Children | length + 1) | string,
                "Parent_Id": city.Id
              } -%}
              {%- set updated_children = city.Children + [new_street] -%}
              {%- set updated_city = city | combine({'Children': updated_children}) -%}
              {%- macro update_nodes(nodes) -%}
                {%- for node in nodes -%}
                  {%- if node.Id == updated_city.Id -%}
                    {{ updated_city }}
                  {%- elif node.Children | length > 0 -%}
                    {{ node | combine({'Children': update_nodes(node.Children)}) }}
                  {%- else -%}
                    {{ node }}
                  {%- endif -%}
                  {%- if not loop.last -%},{%- endif -%}
                {%- endfor -%}
              {%- endmacro -%}
              [{{ update_nodes(nodes) }}]
            {%- else -%}
              {{ nodes }}
            {%- endif -%}
          {%- else -%}
            {{ nodes }}
          {%- endif -%}
      vars:
        city_name: "Augusta"
        street_name: "Third_st"

    - name: Print updated nested structure
      debug:
        var: nested_addresses

代码解释

  1. 创建地址索引:将所有地址项按Id存入字典,方便快速查找父节点对应的子节点。
  2. 构建嵌套结构:从Country节点开始,递归遍历每个节点的子节点(通过Parent_Id匹配),生成包含Children字段的嵌套字典。
  3. 搜索并新增Street:通过自定义宏find_city定位目标City,检查指定Street是否存在,不存在则生成新的Street对象并更新嵌套结构。

最终效果

生成的nested_addresses会是如下层级结构(示例片段):

[
  {
    "Name": "North_America",
    "Type": "Country",
    "Id": "10001",
    "Children": [
      {
        "Name": "Maine",
        "Type": "State",
        "Id": "10011",
        "Children": [
          {
            "Name": "Augusta",
            "Type": "City",
            "Id": "10101",
            "Children": [
              {"Name": "First_st", "Type": "Street", "Id": "11001", "Children": []},
              {"Name": "Second_st", "Type": "Street", "Id": "11002", "Children": []},
              {"Name": "Third_st", "Type": "Street", "Id": "11003", "Children": []}
            ]
          }
        ]
      }
    ]
  }
]

内容的提问来源于stack exchange,提问作者piercjs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 16:16:02