创建子字典键值对的笛卡尔积

Creating cartesian product of sub-dictionary key-val pairs

我有这本字典:

d = {
    'hosts': [{'hostname': 'ijk,uvw,xyz', 'ip': '127.0.0.3,127.0.0.4,127.0.0.5', 'extra': 'check-me,check-this,check-it'},{'hostname': 'abc,def', 'ip': '127.0.0.1,127.0.0.2, 'extra': 'check-for,check-this}]}

我想要一个字典,里面有 key-val 对的笛卡尔积。

我需要从中创建以下字典:

d = {
    'hosts': [
        {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-me'},
        {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-me'},
        {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-me'},
        {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-it'},
        {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-it'},
        {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-it'},
        {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-this'},
        {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-this'},
        {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-this'},
        {'hostname': 'abc', 'ip': '127.0.0.1', 'extra': 'check-for'},
        {'hostname': 'abc', 'ip': '127.0.0.1', 'extra': 'check-this'}
        {'hostname': 'def', 'ip': '127.0.0.2', 'extra': 'check-for'},
        {'hostname': 'def', 'ip': '127.0.0.2', 'extra': 'check-this'}
    ]
}

提取所有可能的值后,您可以使用 itertools.product:

组合它们
for host in d['hosts']:
    hostnames.extend(host['hostname'].split(','))
    ips.extend(host['ip'].split(','))
    extras.extend(host['extra'].split(','))

result = {'hosts': []}
for (hostname, ip, extra) in product(hostnames, ips, extras):
    result['hosts'].append({'hostname': hostname, 'ip': ip, 'extra': extra})

这会产生笛卡尔积,但这意味着您还可以获得主机名和 IP 之间的所有组合。所以你必须跟踪主机名 -> IP 映射:

result = {'hosts': []}
for host in d['hosts']:
    hostnames = host['hostname'].split(',')
    ips = host['ip'].split(',')
    host2ip = {hostname: ip for hostname, ip in zip(hostnames, ips)}
    extras = host['extra'].split(',')

    for (hostname, extra) in product(hostnames, extras):
        result['hosts'].append({'hostname': hostname, 'ip': host2ip[hostname], 'extra': extra})

这给了

{'hosts': [{'extra': 'check-me', 'hostname': 'ijk', 'ip': '127.0.0.3'},
           {'extra': 'check-this', 'hostname': 'ijk', 'ip': '127.0.0.3'},
           {'extra': 'check-it', 'hostname': 'ijk', 'ip': '127.0.0.3'},
           {'extra': 'check-me', 'hostname': 'uvw', 'ip': '127.0.0.4'},
           {'extra': 'check-this', 'hostname': 'uvw', 'ip': '127.0.0.4'},
           {'extra': 'check-it', 'hostname': 'uvw', 'ip': '127.0.0.4'},
           {'extra': 'check-me', 'hostname': 'xyz', 'ip': '127.0.0.5'},
           {'extra': 'check-this', 'hostname': 'xyz', 'ip': '127.0.0.5'},
           {'extra': 'check-it', 'hostname': 'xyz', 'ip': '127.0.0.5'},
           {'extra': 'check-for', 'hostname': 'abc', 'ip': '127.0.0.1'},
           {'extra': 'check-this', 'hostname': 'abc', 'ip': '127.0.0.1'},
           {'extra': 'check-for', 'hostname': 'def', 'ip': '127.0.0.2'},
           {'extra': 'check-this', 'hostname': 'def', 'ip': '127.0.0.2'}]}

一种方法:

from itertools import product, chain


def f(d):
    t = product(zip(*(z := [d[k].split(',') for k in d])[:2]), z[2])
    return [dict(zip(d, (*x, y))) for x, y in t]

{'hosts': [*chain(*map(f, d['hosts']))]}

输出:

{'hosts': [{'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-me'},
           {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-this'},
           {'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-it'},
           {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-me'},
           {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-this'},
           {'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-it'},
           {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-me'},
           {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-this'},
           {'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-it'},
           {'hostname': 'abc', 'ip': '127.0.0.1', 'extra': 'check-for'},
           {'hostname': 'abc', 'ip': '127.0.0.1', 'extra': 'check-this'},
           {'hostname': 'def', 'ip': '127.0.0.2', 'extra': 'check-for'},
           {'hostname': 'def', 'ip': '127.0.0.2', 'extra': 'check-this'}]}