如何展平嵌套字典?

问题描述 投票:0回答:1

我有一个字典

foo:
  a:
    aa1: 1
    aa2: 2
  b:
    bb1: 3
    bb2: 4

我想将其转换为:

- {key:a, subkey:aa1, value: 1}
- {key:a, subkey:aa2, value: 2}
- {key:b, subkey:bb1, value: 3}
- {key:b, subkey:bb2, value: 4}

如果只能用 json_query 实现,我会接受。但最好不要使用复杂的 JMESPath 公式。

ansible
1个回答
2
投票

更新

使用过滤器community.general.dict

  foo_arr: "{{ foo|ansible.utils.to_paths|dict2items }}"
  foo_key: "{{ foo_arr|map(attribute='key')|map('split', '.') }}"
  foo_val: "{{ foo_arr|map(attribute='value') }}"
  bar: "{{ foo_key|zip(foo_val)|map('flatten')
                  |map('zip', ['key', 'subkey', 'value'])
                  |map('map', 'reverse')
                  |map('community.general.dict') }}"

给你想要的东西

  bar:
    - {key: a, subkey: aa1, value: 1}
    - {key: a, subkey: aa2, value: 2}
    - {key: b, subkey: bb1, value: 3}
    - {key: b, subkey: bb2, value: 4}

注意:此解决方案仅限于不带点的按键。


用于测试的完整剧本示例

- hosts: all

  vars:

    foo:
      a:
        aa1: 1
        aa2: 2
      b:
        bb1: 3
        bb2: 4

    foo_arr: "{{ foo|ansible.utils.to_paths|dict2items }}"
    foo_key: "{{ foo_arr|map(attribute='key')|map('split', '.') }}"
    foo_val: "{{ foo_arr|map(attribute='value') }}"
    bar: "{{ foo_key|zip(foo_val)|map('flatten')
                    |map('zip', ['key', 'subkey', 'value'])
                    |map('map', 'reverse')
                    |map('community.general.dict') }}"

  tasks:

    - debug:
        var: bar|to_yaml

起源

例如,

    - set_fact:
        bar: "{{ bar|d([]) + [{'key': _key,
                               'subkey': _sub,
                               'value': _val|int}] }}"
      loop: "{{ foo|ansible.utils.to_paths|dict2items }}"
      vars:
        _arr: "{{ item.key.split('.') }}"
        _key: "{{ _arr.0 }}"
        _sub: "{{ _arr.1 }}"
        _val: "{{ item.value }}"

给予

  bar:
    - {key: a, subkey: aa1, value: 1}
    - {key: a, subkey: aa2, value: 2}
    - {key: b, subkey: bb1, value: 3}
    - {key: b, subkey: bb2, value: 4}

问:密钥可能是IP地址,因此它损坏了。

A:您可以通过索引轻松解决这个问题,例如

      vars:
        _arr: "{{ item.key.split('.') }}"
        _key: "{{ _arr[:-1]|join('.') }}"
        _sub: "{{ _arr[-1] }}"
        _val: "{{ item.value }}"

下一个选项是编写一个过滤器。例如,使用下面的过滤器并设置另一个 separator

def dict_flatten(d, separator='.'):
    out = {}
    def flatten(x, name=''):
        if type(x) is dict:
            for a in x:
                flatten(x[a], name + a + separator)
        elif type(x) is list:
            i = 0
            for a in sorted(x):
                flatten(a, name + str(i) + separator)
                i += 1
        else:
            out[name[:-1]] = x
    flatten(d)
    return out

查看源代码

© www.soinside.com 2019 - 2024. All rights reserved.