首页 > 解决方案 > 减少由布尔运算符定义的嵌套字典

问题描述

假设我有一个如下所示的有效负载:

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]

在这一点上,我可以使用字符串ANDOR布尔运算符,感觉好像它们分别映射到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,有什么建议吗?

标签: pythonalgorithmrecursionreduce

解决方案


您可以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',)}

推荐阅读