我最近不得不用嵌套的字典/列表组合解决实际数据系统中的问题。我在这方面工作了很长一段时间并想出了一个解决方案,但我非常不满意。我不得不求助于使用globals()
和一个命名的临时全局参数。
我不喜欢使用全局变量。这只是要求注射漏洞。我觉得必须有更好的方法来执行这项任务而不诉诸全局变量。
问题数据集:
d = {
"k":1,
"stuff":"s1",
"l":{"m":[
{
"k":2,
"stuff":"s2",
"l":None
},
{
"k":3,
"stuff":"s3",
"l":{"m":[
{
"k":4,
"stuff":"s4",
"l":None
},
{
"k":5,
"stuff":"s5",
"l":{"m":[
{
"k":6,
"stuff":"s6",
"l":None
},
]}
},
]}
},
]}
}
期望的输出:
[{'k': 1, 'stuff': 's1'},
{'k': 2, 'stuff': 's2'},
{'k': 3, 'stuff': 's3'},
{'k': 4, 'stuff': 's4'},
{'k': 5, 'stuff': 's5'},
{'k': 6, 'stuff': 's6'}]
我的解决方案:
def _get_recursive_results(d, iter_key, get_keys):
if not 'h' in globals():
global h
h = []
h.append({k:d.get(k) for k in get_keys})
d2 = d.copy()
for k in iter_key:
if not d2:
continue
d2 = d2.get(k)
for td in d2:
d3 = td.copy()
for k in iter_key:
if not d3:
continue
d3 = d3.get(k)
if d3:
return _get_recursive_results(td, iter_key, get_keys)
h.append({k:td.get(k) for k in get_keys})
else:
l = [k for k in h]
del globals()['h']
return l
按如下方式调用我的函数会返回所需的结果:
_get_recursively(d, ['l','m'], ['k','stuff'])
我如何构建更好的解决方案?
答案 0 :(得分:7)
这是一个稍微修改过的版本,没有使用全局变量。将h
设为None
默认情况下,为第一次调用_get_recursive_results()
创建一个新列表。稍后在h
的递归调用中提供_get_recursive_results()
作为参数:
def _get_recursive_results(d, iter_key, get_keys, h=None):
if h is None:
h = []
h.append({k:d.get(k) for k in get_keys})
d2 = d.copy()
for k in iter_key:
if not d2:
continue
d2 = d2.get(k)
for td in d2:
d3 = td.copy()
for k in iter_key:
if not d3:
continue
d3 = d3.get(k)
if d3:
return _get_recursive_results(td, iter_key, get_keys, h)
h.append({k:td.get(k) for k in get_keys})
else:
l = [k for k in h]
return l
现在:
>>> _get_recursive_results(d, ['l','m'], ['k','stuff'])
[{'k': 1, 'stuff': 's1'},
{'k': 2, 'stuff': 's2'},
{'k': 3, 'stuff': 's3'},
{'k': 4, 'stuff': 's4'},
{'k': 5, 'stuff': 's5'},
{'k': 6, 'stuff': 's6'}]
不需要复制中间词。这是一个没有复制的进一步修改版本:
def _get_recursive_results(d, iter_key, get_keys, h=None):
if h is None:
h = []
h.append({k: d.get(k) for k in get_keys})
for k in iter_key:
if not d:
continue
d = d.get(k)
for td in d:
d3 = td
for k in iter_key:
if not d3:
continue
d3 = d3.get(k)
if d3:
return _get_recursive_results(td, iter_key, get_keys, h)
h.append({k: td.get(k) for k in get_keys})
else:
return h
答案 1 :(得分:5)
这不是通用的,但可以完成工作:
def parse_tree(d, keys):
result = [{key: d[key] for key in keys}]
l = d.get('l', None)
if l is not None:
entries = l.get('m', [])
for entry in entries:
result.extend(parse_tree(entry))
return result
>>> parse_tree(d, ['k', 'stuff'])
[{'k': 1, 'stuff': 's1'},
{'k': 2, 'stuff': 's2'},
{'k': 3, 'stuff': 's3'},
{'k': 4, 'stuff': 's4'},
{'k': 5, 'stuff': 's5'},
{'k': 6, 'stuff': 's6'}]
答案 2 :(得分:4)
使用以下生成器:
def get_stuff(dct, iter_keys, get_keys):
k, stuff = get_keys
l, m = iter_keys
if k in dct:
yield {k: dct[k], stuff: dct[stuff]}
if dct.get(l):
for subdct in dct[l][m]:
for res in get_stuff(subdct, iter_keys, get_keys):
yield res
list(get_stuff(d, ["l", "m"], ["k", "stuff"]))
你得到的结果是:
list(get_stuff(d))
Python 3.3提供了用于委托屈服于子生成器的新yield from
表达式。使用此表达式,代码可以缩短一行:
def get_stuff(dct):
if "k" in dct:
yield {"k": dct["k"], "stuff": dct["stuff"]}
if dct.get("l"):
for subdct in dct["l"]["m"]:
yield from get_stuff(subdct)
def get_stuff(dct, iter_keys, get_keys):
k, stuff = get_keys
l, m = iter_keys
if k in dct:
yield {k: dct[k], stuff: dct[stuff]}
if dct.get(l):
for subdct in dct[l][m]:
yield from get_stuff(subdct, iter_keys, get_keys):
globals
通常,如果您需要构建列表并搜索替换全局变量,则生成器可能会 方便,因为他们将当前工作的状态保持在局部变量中,加上构建整个结果会推迟到消耗生成的值。
递归将子结果存储在堆栈中的局部变量中。
一个类可以作为封装变量的锡。
您可以将中间结果存储在实例属性中,而不是使用全局变量。
在您提到的评论中,每次转储都会收到许多不同的类型。
我认为您的数据符合以下期望:
{"k": xx, "stuff": yy}
)使解决方案更加通用的一个选项是提供要使用的密钥列表 要访问值/子项,另一个选项是提供一个函数 获取节点值和子项的工作。
在这里,我使用get_value
来传递节点值,并使用get_subitems
来传递子节点:
def get_value(data):
try:
return {"k": data["k"], "stuff": data["stuff"]}
except KeyError:
return None
def get_subitems(data):
try:
return data["l"]["m"]
except TypeError:
return None
然后通过以下方式完成处理:
def get_stuff(dct, get_value_fun, get_subitems_fun):
value = get_value(dct)
if value:
yield value
lst = get_subitems_fun(dct)
if lst:
for subdct in lst:
for res in get_stuff(subdct, get_value_fun, get_subitems_fun):
yield res
以这种方式打电话:
get_stuff(d, get_value, get_subitems)
使用函数的优点在于它对于任何数据都更加灵活
您必须处理的结构(适应其他数据结构只需要提供自定义版本的函数get_value
和get_subitems
- 根据您的偏好设置相同或不同的名称。
答案 3 :(得分:3)
编辑:第一个版本有一个现已更正的错误
我相信这应该有效,我们正在使用递归的力量!
def strip_leaves_from_tree(my_tree):
result = list()
row = dict()
for key in my_tree:
child = my_tree[key]
if type(child) in (int, str,):
row[key] = child
elif isinstance(child, dict):
result = strip_leaves_from_tree(child)
elif isinstance(child, list):
for element in child:
result += strip_leaves_from_tree(element)
if row: result = [row,]+result
return result
答案 4 :(得分:2)
我确认它有效。请检查一下。 当然,在更改字典列表的结构时应该修改它。
def add(ret, val):
if val is not None: ret.append(val)
def flatten(d, ret):
for k,v in d.items():
if isinstance(v, dict): add(ret,flatten(v, ret))
elif isinstance(v, list):
for i in v: add(ret, flatten(i, ret))
elif k=='k':
ret.append({'k':v,'stuff':d.get('stuff')})
ret = []
flatten(d, ret)
答案 5 :(得分:1)