减少布尔运算符定义的嵌套字典

时间:2019-08-11 23:46:17

标签: python algorithm recursion reduce

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

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()

我在嵌套的bool_operator字典中发表了评论:[...]块基于运算符如何减少每个“块”。

鉴于所有这些,我想将此有效负载减少到以下水平:

[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
        )

这对于单个布尔运算符和包含值的列表列表非常有效。但是,我在递归方面遇到了麻烦。有什么建议么?我使这个复杂化了吗?希望将其变成一个整齐的库以供使用。

在此先感谢您的见解。

更新8/12/2019

@ Ajax1234的解决方案效果很好,直到遇到一种情况,对于给定的布尔运算符,例如有两个以上的块,

payload = {'OR': [
              {'AND': [
                        [
                             ('5657',),
                             ('5698',)               
                        ]
                   ]
              },
              {'AND': [
                        [
                             ('6101',),
                             ('5420',),
                             ('5334',),
                             ('5439',)
                        ]
                   ]
              },
              {'AND': [
                        [
                             ('5802',),
                             ('6005',),     
                             ('6675',),
                             ('5878',)
                        ]
                   ]
              },
              {'AND': [
                        [
                             ('6265',),
                             ('6157',),
                             ('6238',),
                             ('6189',),
                             ('6191',)
                        ]
                   ]
              }
        ]
    }

要更新要求:理想情况下, 也可以在上述有效负载上工作。

更新8/13/2019

仍然遇到极端情况,例如以下有效载荷:

payload = {
    'AND': [
        {
            'OR': [
                [('6101',)],
                [('6265',)]
            ]
        }
    ]
}

在这种情况下,我的输出是单值集:{'OR'},尽管我希望有{('6101',),('6265',)}。到目前为止,感谢您的帮助@ ajax1234,有什么建议吗?

1 个答案:

答案 0 :(得分:1)

您可以将functools.reducegetattr一起使用:

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