很难搜索到我所描述的内容,所以如果这个问题已经在其他地方得到了回答,我深表歉意。
我有一个来自cli工具(timewarrior,如果你熟悉的话!)的字典列表,我想将它们组织成一个层次结构,以便打印表或导出CSV。
如何生成层次结构取决于每个列表元素(称为"tags"中包含的列表的顺序。每个标签列表包含一些跟踪时间。我想总结一下从下到上的层次结构所花费的时间。
下面是我正在处理的一个过于简化的数据示例:
data = [{"tags": ["Project A", "Task 1"], "time": 50},
{"tags": ["Project A", "Task 2"], "time": 20},
{"tags": ["Do a thing"], "time": 10},
{"tags": ["Project B", "Do a thing"], "time": 50}]
使用这些数据,我希望为递归函数调用创建以下两个结构之一:
嵌套列表:
outcome_a = [["Project A", 70, [["Task 1", 50], ["Task 2", 20]]],
["Do a thing", 10],
["Project B", 50, ["Do a thing", 50]]]
或嵌套字典:
outcome_b = {
"Project A": {
"time": 70,
"sub": {
"Task 1": {
"time": 50
},
"Task 2": {
"time": 20
}
}
},
"Do a thing": {
"time": 10
},
"Project B": {
"time": 50,
"sub": {
"Do a thing": {
"time": 50
}
}
}
}
遍历字典,然后遍历其中的标记,感觉很简单。让我困惑的是,一旦迭代越过了第一层嵌套数据,如何优雅地跟踪每个元素的时间上下文。
我显然不想重复列表并试图重新发现上下文。我能想到的最佳解决方案是迭代数据,并以某种方式将上下文传递给下一个tags
元素。我认为某种递归函数或减速器可以解决这个问题。
如果您连续访问它,我会避免嵌套的list
。
如果你使用dict.get
,你的dict
结果很容易。
data = [{"tags": ["Project A", "Task 1"], "time": 50},
{"tags": ["Project A", "Task 2"], "time": 20},
{"tags": ["Do a thing"], "time": 10},
{"tags": ["Project B", "Do a thing"], "time": 50}]
result = {}
for tags in data:
project, *task = tags['tags']
task_time = tags['time']
if task:
result[project] = result.get(project, {'time': 0, 'sub': {}})
result[project]['sub'][task[0]] = {'time': task_time}
result[project]['time'] += task_time
else:
result[project] = {'time': task_time}
print(result)
# {
# 'Project A': {
# 'time': 70,
# 'sub': {
# 'Task 1': {'time': 50},
# 'Task 2': {'time': 20}
# }
# },
# 'Do a thing': {'time': 10},
# 'Project B': {
# 'time': 50,
# 'sub': {
# 'Do a thing': {'time': 50}
# }
# }
# }
一些最突出的结果:
这将'tags'
键解包为2个变量。如果list
中在该键下只有一个项,则将一个空的[]
分配给task
。
project, *task = tags['tags']
我们知道在这个分支中需要'sub'
键。这里使用dict.get()
来检索project
键。如果不存在,则为其分配一个默认的dict
结构体。
result[project] = result.get(project, {'time': 0, 'sub': {}})
由于result[project]['time']
的初始值被设置为0
。
result[project]['time'] += task_time
最后,对于单个任务,当task
为" false "
result[project] = {'time': task_time}
编辑:
对于标签大小不可知的东西,你可以使用相同的dict.get
技巧,并将其嵌入任意深度。它还有一个额外的好处,就是删除了if
语句。
data = [{"tags": ["Project A", "Task 1"], "time": 50},
{"tags": ["Project A", "Task 2", "Part 1"], "time": 10},
{"tags": ["Project A", "Task 2", "Part 2"], "time": 10},
{"tags": ["Do a thing"], "time": 10},
{"tags": ["Project B", "Do a thing"], "time": 50}]
result = {}
for tags in data:
project, *tasks = tags['tags']
task_time = tags['time']
result[project] = result.get(project, {'time': 0})
result[project]['time'] += task_time
current_dict = result[project]
for task in tasks:
current_dict['sub'] = current_dict.get('sub', {})
current_dict = current_dict['sub']
current_dict[task] = current_dict.get(task, {'time': 0})
current_dict[task]['time'] += task_time
current_dict = current_dict[task]
print(result)
# {
# 'Project A': {
# 'time': 70,
# 'sub': {
# 'Task 1': {'time': 50},
# 'Task 2': {
# 'time': 20,
# 'sub': {
# 'Part 1': {'time': 10},
# 'Part 2': {'time': 10}
# }
# }
# }
# },
# 'Do a thing': {'time': 10},
# 'Project B': {
# 'time': 50,
# 'sub': {
# 'Do a thing': {'time': 50}
# }
# }
# }
我只是想发表我自己的答案。再次感谢@Axe319帮我摆脱困境。
我想看到嵌套列表发生,所以它是:
DATA = [{"tags": ["Project A", "Task 1"], "time": 50},
{"tags": ["Project A", "Task 2", "Part 1"], "time": 10},
{"tags": ["Project A", "Task 2", "Part 2"], "time": 10},
{"tags": ["Do a thing"], "time": 10},
{"tags": ["Project B", "Do a thing"], "time": 50}]
ROWS = []
def update_row_data(rows, task):
levels = task["tags"]
time = task["time"]
level, *nested = levels
row = -1
for i, t in enumerate(rows):
if t[0] == level:
row = i
break
if row > -1:
rows[row][1] += time
else:
rows.append([level, time, []])
row = len(rows) - 1
if nested:
next = {"tags": nested, "time": time}
rows[row][2] = update_row_data(rows[row][2], next)
return rows
for entry in DATA:
ROWS = update_row_data(ROWS, entry)
print(ROWS)