python - 减少由布尔运算符定义的嵌套字典
问题描述
假设我有一个如下所示的有效负载:
payload = {
"OR": [
{
"AND" : [[1,2,3],[3,4]]
}, # ([1,2,3] AND [3,4]) --> [3]
{
"OR" : [
{
"AND" : [
{
"OR" : [[10,11],[12,13]]
}, # ([10,11] OR [12,13]) ---> [10,11,12,13]
{
"AND": [[11,13]]
} # ([11,13]) ---> [11,13]
]
}, # ([10,11,12,13] AND [11,13]) ---> [11,13]
{
"AND" : [[14,15],[15]]
} # ([14,15] AND [15]) ---> [15]
]
} # ([11,13] OR [15]) ---> [11,13,15]
]
} # ([3] OR [11,13,15]) ---> [3,11,13,15]
在这一点上,我可以使用字符串AND
和OR
布尔运算符,感觉好像它们分别映射到set.intersection()
和set.union()
。
我在nested bool_operator
: [...]
blocks 的字典中评论了每个“块”如何根据运算符减少。
鉴于这一切,我想将此有效负载减少到以下内容:
[3,11,13,15]
我理解这需要递归,并为此创建了一个函数,该函数在给定纯文本布尔运算符的情况下减少列表列表:
from functools import reduce
def reduce_block(bool_operator, block):
bool_operator_set_hash = {
'AND':'intersection',
'OR':'union'
}
return reduce(
lambda x,y: getattr(set(x),bool_operator_set_hash.get(bool_operator.upper()))(set(y)),
block
)
这适用于单个布尔运算符和包含值的列表列表。但是,我在递归方面遇到了麻烦。有什么建议么?我是否过于复杂了?想把它变成一个整洁的库以供使用。
非常感谢您的任何见解。
2019 年 8 月 12 日更新
@Ajax1234 的解决方案运行良好,直到我遇到给定布尔运算符有 > 2 个块的情况,例如
payload = {'OR': [
{'AND': [
[
('5657',),
('5698',)
]
]
},
{'AND': [
[
('6101',),
('5420',),
('5334',),
('5439',)
]
]
},
{'AND': [
[
('5802',),
('6005',),
('6675',),
('5878',)
]
]
},
{'AND': [
[
('6265',),
('6157',),
('6238',),
('6189',),
('6191',)
]
]
}
]
}
更新要求:理想情况下也可以在上述有效负载上工作。
2019 年 8 月 13 日更新
仍然遇到边缘情况,例如以下有效负载:
payload = {
'AND': [
{
'OR': [
[('6101',)],
[('6265',)]
]
}
]
}
在这种情况下,我的输出是单值集:{'OR'}
,尽管我希望{('6101',),('6265',)}
. 到目前为止感谢您的帮助@ajax1234,有什么建议吗?
解决方案
您可以functools.reduce
使用getattr
:
from functools import reduce
def _eval(_load):
[[op, _vals]] = _load.items()
if len(_vals) == 1:
return set(_vals[0]) if not isinstance(_vals[0], dict) else _eval(_vals[0])
return reduce(lambda l, r:getattr(_eval(l) if isinstance(l, dict) else set(l), f'__{op.lower()}__')(_eval(r) if isinstance(r, dict) else set(r)), _vals)
print(_eval(payload))
输出:
{11, 3, 13, 15}
print(_eval(new_payload))
输出:
{('5878',), ('6265',), ('5698',), ('6189',), ('5334',), ('5439',), ('6238',), ('5420',), ('5802',), ('6157',), ('6101',), ('6191',), ('6005',), ('6675',), ('5657',)}
print(_eval(_new_payload))
输出:
{('6265',), ('6101',)}
推荐阅读
- python - Python BeautifulSoup 在特定标签之后提取文本
- python - 聚合数据框上的列,根据另一个数据框对其进行分组,而不合并它们
- java - 由于内容安全策略,无法在 Iframe 中加载 BIRT 报告的图像
- nginx - 将 prerender.io 与我现有的 nginx 配置一起使用
- mysql - 仅比较最新记录
- powershell - 如何避免 Windows PowerShell 中的回显创建二进制文件?
- javascript - 消除 Highchart Bulletchart 中一系列值末尾的差距?
- npm - NPM 过时抛出 npm ERR!尚未实施
- javascript - 根据居中的滑块项目显示标题
- java - Maven/SpringBoot Web 应用程序:如何在后端应用更改?