python 中任意长度的嵌套字典的组合
Combination of nested dictionaries with arbitrary lengths in python
我正在寻找一个函数,它将采用嵌套字典,并生成值的组合/乘积。
我的查询与此处指定的问题类似,但我似乎无法根据我的需要调整答案:
我希望有这样的输入:
d = {
"country": [1, 2],
"health_state": [
{"healthy": [1]},
{"breast cancer": {"stage": [1, 2]}}
]
}
产生如下输出:
[
{{"country":1},{"health state":{"healthy":1}}},
{{"country":2},{"health state":{"healthy":1}}},
{{"country":1},{"health state":{"breast cancer":{"stage":1}}}},
{{"country":1},{"health state":{"breast cancer":{"stage":2}}}},
{{"country":2},{"health state":{"breast cancer":{"stage":1}}}},
{{"country":2},{"health state":{"breast cancer":{"stage":2}}}}
]
在这个例子中,输出的是一个人可以占据'states'的列表
- 列表(输入)的任何两个元素不应在返回列表(输出)的同一元素中,例如某人不能同时在国家 1 和国家 2
- 字典(输入)中的所有键都应在列表(输出)的相同元素中返回,例如有人在国家 1,也在 health_state。如果该健康状态为 'breast cancer',则它们也处于第 1 阶段或第 2 阶段
我可以设想一个需要大量 for 循环的解决方案,检查元素是字典、列表还是两者都不是,但这似乎效率低下,尤其是对于深度嵌套的字典。我怀疑可能有使用 itertools.product
和递归的更优雅的解决方案?
您可以使用递归 itertools.product
:
import itertools as it
d = {'country': [1, 2], 'health_state': [{'healthy': [1]}, {'breast cancer': {'stage': [1, 2]}}]}
def c_prod(d):
if isinstance(d, list):
for i in d:
yield from ([i] if not isinstance(i, (dict, list)) else c_prod(i))
else:
for i in it.product(*map(c_prod, d.values())):
yield dict(zip(d.keys(), i))
print(list(c_prod(d)))
输出:
[{'country': 1, 'health_state': {'healthy': 1}},
{'country': 1, 'health_state': {'breast cancer': {'stage': 1}}},
{'country': 1, 'health_state': {'breast cancer': {'stage': 2}}},
{'country': 2, 'health_state': {'healthy': 1}},
{'country': 2, 'health_state': {'breast cancer': {'stage': 1}}},
{'country': 2, 'health_state': {'breast cancer': {'stage': 2}}}]
上面代码的输出生成了一个字典列表,但是您想要的输出反映了一个字典列表列表 (list[list[dict]]
),因此,可以进行最终转换:
r = [[{j:k} for j, k in i.items()] for i in c_prod(d)]
输出:
[[{'country': 1}, {'health_state': {'healthy': 1}}], [{'country': 1}, {'health_state': {'breast cancer': {'stage': 1}}}], [{'country': 1}, {'health_state': {'breast cancer': {'stage': 2}}}], [{'country': 2}, {'health_state': {'healthy': 1}}], [{'country': 2}, {'health_state': {'breast cancer': {'stage': 1}}}], [{'country': 2}, {'health_state': {'breast cancer': {'stage': 2}}}]]
我正在寻找一个函数,它将采用嵌套字典,并生成值的组合/乘积。
我的查询与此处指定的问题类似,但我似乎无法根据我的需要调整答案:
我希望有这样的输入:
d = {
"country": [1, 2],
"health_state": [
{"healthy": [1]},
{"breast cancer": {"stage": [1, 2]}}
]
}
产生如下输出:
[
{{"country":1},{"health state":{"healthy":1}}},
{{"country":2},{"health state":{"healthy":1}}},
{{"country":1},{"health state":{"breast cancer":{"stage":1}}}},
{{"country":1},{"health state":{"breast cancer":{"stage":2}}}},
{{"country":2},{"health state":{"breast cancer":{"stage":1}}}},
{{"country":2},{"health state":{"breast cancer":{"stage":2}}}}
]
在这个例子中,输出的是一个人可以占据'states'的列表
- 列表(输入)的任何两个元素不应在返回列表(输出)的同一元素中,例如某人不能同时在国家 1 和国家 2
- 字典(输入)中的所有键都应在列表(输出)的相同元素中返回,例如有人在国家 1,也在 health_state。如果该健康状态为 'breast cancer',则它们也处于第 1 阶段或第 2 阶段
我可以设想一个需要大量 for 循环的解决方案,检查元素是字典、列表还是两者都不是,但这似乎效率低下,尤其是对于深度嵌套的字典。我怀疑可能有使用 itertools.product
和递归的更优雅的解决方案?
您可以使用递归 itertools.product
:
import itertools as it
d = {'country': [1, 2], 'health_state': [{'healthy': [1]}, {'breast cancer': {'stage': [1, 2]}}]}
def c_prod(d):
if isinstance(d, list):
for i in d:
yield from ([i] if not isinstance(i, (dict, list)) else c_prod(i))
else:
for i in it.product(*map(c_prod, d.values())):
yield dict(zip(d.keys(), i))
print(list(c_prod(d)))
输出:
[{'country': 1, 'health_state': {'healthy': 1}},
{'country': 1, 'health_state': {'breast cancer': {'stage': 1}}},
{'country': 1, 'health_state': {'breast cancer': {'stage': 2}}},
{'country': 2, 'health_state': {'healthy': 1}},
{'country': 2, 'health_state': {'breast cancer': {'stage': 1}}},
{'country': 2, 'health_state': {'breast cancer': {'stage': 2}}}]
上面代码的输出生成了一个字典列表,但是您想要的输出反映了一个字典列表列表 (list[list[dict]]
),因此,可以进行最终转换:
r = [[{j:k} for j, k in i.items()] for i in c_prod(d)]
输出:
[[{'country': 1}, {'health_state': {'healthy': 1}}], [{'country': 1}, {'health_state': {'breast cancer': {'stage': 1}}}], [{'country': 1}, {'health_state': {'breast cancer': {'stage': 2}}}], [{'country': 2}, {'health_state': {'healthy': 1}}], [{'country': 2}, {'health_state': {'breast cancer': {'stage': 1}}}], [{'country': 2}, {'health_state': {'breast cancer': {'stage': 2}}}]]