[英]Recursive function to check dictionary is a subset of another dictionary
我想递归地检查一个字典是否是另一个字典的子集。 让我们假设两个字典都有内置类型作为项目。
我已经看到已经有一个非常古老的线程Python: Check if one dictionary is a subset of another large dictionary<\/a>试图解决类似但不完全的问题......因为那里的答案都不能满足我的目的决定在那里发布我自己的解决方案<\/a>,但它仍然没有完全完成,下面的函数在几乎所有情况下都可以正常工作,但在子集具有超集中不存在的值的情况下,它会惨遭失败,即:
def is_subset(superset, subset):
for key, value in superset.items():
if key not in subset:
continue
if isinstance(value, dict):
if not is_subset(value, subset[key]):
return False
elif isinstance(value, str):
if not subset[key] in value:
return False
elif isinstance(value, list):
if not set(subset[key]) <= set(value):
return False
elif isinstance(value, set):
if not subset[key] <= value:
return False
else:
if not subset[key] == value:
return False
return True
superset = {'question': 'mcve', 'metadata': {}}
subset = {'question': 'mcve', 'metadata': {'author': 'BPL'}}
print(is_subset(superset, subset))
您的代码逻辑是颠倒的。 注意如何获取超 集中的每个元素,如果不在子集中 ,则继续。 您要做的是获取子集中的每个元素,并检查它们是否在超 集中 。
这是您代码的固定版本。
def is_subset(superset, subset):
for key, value in subset.items():
if key not in superset:
return False
if isinstance(value, dict):
if not is_subset(superset[key], value):
return False
elif isinstance(value, str):
if value not in superset[key]:
return False
elif isinstance(value, list):
if not set(value) <= set(superset[key]):
return False
elif isinstance(value, set):
if not value <= superset[key]:
return False
else:
if not value == superset[key]:
return False
return True
这是给出正确结果的函数的一些示例。
superset = {'question': 'mcve', 'metadata': {}}
subset = {'question': 'mcve', 'metadata': {'author': 'BPL'}}
is_subset(superset, subset) # False
superset = {'question': 'mcve', 'metadata': {'foo': {'bar': 'baz'}}}
subset = {'metadata': {'foo': {}}}
is_subset(superset, subset) # True
superset = {'question': 'mcve', 'metadata': {'foo': 'bar'}}
subset = {'question': 'mcve', 'metadata': {}, 'baz': 'spam'}
is_subset(superset, subset) # False
只是一个猜测,但我认为由于超集中“元数据”返回的dic是空的,因此if语句均不会返回true,因此您将最终返回True。
您可以检查一下dic的长度是否为零。 如果一个不是另一个,则返回false。 否则,请继续执行递归解决方案。
这是一个也可以正确地递归到列表和集合中的解决方案。 (我更改了论据的顺序,因为这对我来说更有意义)
def is_subset(subset, superset):
if isinstance(subset, dict):
return all(key in superset and is_subset(val, superset[key]) for key, val in subset.items())
if isinstance(subset, list) or isinstance(subset, set):
return all(any(is_subset(subitem, superitem) for superitem in superset) for subitem in subset)
if isinstance(subset, str):
return subset in superset
# assume that subset is a plain value if none of the above match
return subset == superset
我不太喜欢原来的解决方案——正如一些评论所说,它不适用于我的一些案例。 这是一个更通用的解决方案:
def is_subvalue(supervalue, subvalue) -> bool:
"""Meant for comparing dictionaries, mainly.
Note - I don't treat ['a'] as a subvalue of ['a', 'b'], or 'a' as a subvalue of 'ab'.
For that behavior for a list or set, remove the line: `if len(supervalue) != len(subvalue): return False`
For that behavior for a string, switch `subvalue == supervalue` to `subvalue in supervalue` for strings only.
But NOT in this function, as it's meant to compare dictionaries and {'ab': 'a'} is not the same as {'a': 'a'}
"""
if isinstance(subvalue, list) or isinstance(subvalue, set):
if isinstance(subvalue, list) and not isinstance(supervalue, list):
return False
if isinstance(subvalue, set) and not isinstance(supervalue, set):
return False
if len(supervalue) != len(subvalue):
return False
return all([is_subvalue(supervalue[i], subvalue[i]) for i in range(len(subvalue))])
if isinstance(subvalue, dict):
if not isinstance(supervalue, dict):
return False
for key in subvalue:
if key not in supervalue or not is_subvalue(supervalue[key], subvalue[key]):
return False
return True
# all other types.
return supervalue == subvalue
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.