将字典列表整理成嵌套列表



很难搜索到我所描述的内容,所以如果这个问题已经在其他地方得到了回答,我深表歉意。

我有一个来自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)

相关内容

  • 没有找到相关文章

最新更新