我有一个奇怪的数据结构,它是由我无法控制的外部服务返回给我的。
数据本质上是一个字典列表,但以一种奇怪的方式分割:它作为字典列表返回,其中每个字典都有一个单个键。从这个列表中提取多个元素会生成字典中的所有键。
代码中:
[ {'id': 1}, {'a': a}, {'b': b}, {'c': c},
{'id': 2}, {'a': a}, {'b': b}, {'c': c},
{'id': 3}, {'a': a}, {'b': b}, {'c': c},
...
]
我想要重构的每一个字典都是从id
字典开始的。找到id
键后,我需要从列表中获取所有值,直到找到另一个id
。
我目前的解决方案是:
def split_groups(data, key='id'):
groups = []
for e in data:
if key in e: # begin new group
groups.append(list())
groups[-1].append(e)
return groups
这很管用,但很难看。我知道itertools.groupby
:但是,我真的不知道如何使用它
这条线的结果:
[(k, list(g)) for k, g in groupby(data, lambda d: d.get('id') is not None)]
是:
[(True, [{'id': 1}]),
(False, [{'a': 1}, {'b': 2}, {'c': 3}]),
(True, [{'id': 2}]),
(False, [{'a': 1}, {'b': 2}, {'c': 3}]),
(True, [{'id': 3}]),
(False, [{'a': 1}, {'b': 2}, {'c': 3}])]
正如您所看到的,id
字典最终与以下值处于不同的组中。
我做错了什么?
在Sumukh Barve的回答之后,我想groupby不是适合我工作的工具。我当前的代码将用于生产;只是为了好玩,我把它改写成这样:
def split_groups(data, key='id'):
if not data:
return []
predicate = lambda d: key not in d
head, tail = data[0], data[1:]
group = [[head] + list(takewhile(predicate, tail))]
rest = list(dropwhile(predicate, tail))
group.extend(split_groups(rest, key))
return group
这是一种效率低得多、可读性差得多、更吸引强迫症患者的形式
感谢大家的帮助!
为了防止有一天有人碰到我的同一个问题,我附上了完整的解决方案和一些示例数据。
从文档:
它(
itertools.groupby
)每次键函数的值发生变化时都会生成一个break或新组。
从这个意义上讲,itertools.groupby
类似于str.split
;不同之处在于分割序列也包括在输出中。
"1,2,3".split(",") ==> ["1", "2", "3"]
"1,2,3".splitLikeGroupBy(",") ==> ["1", ",", "2", ",", "3"]
所以,你没有做错任何事
另外,我认为你的解决方案很好。
但是,如果您坚持使用itertools.groupby
,请尝试以下操作:
a = [(k, list(g)) for k, g in groupby(data, lambda d: d.get('id') is not None)];
[a[i][1] + a[i+1][1] for i in range(len(a)) if i % 2 == 0]
第一行直接来自您的代码。第二是一些简单的处理。
建议:
您可能希望使用多元素词典列表,而不是使用单元素词典列表。
也就是说,而不是使用这个:
[
[{"id": "id1"}, {"a": "a1"}],
[{"id": "id2"}, {"a": "a2"}], ...
]
你可能想使用这个:
[
{"id": "id1", "a": "a1"},
{"id": "id2", "a": "a2"}, ...
]
希望这能有所帮助
IMHO,这不是一项琐碎的任务。两条线的解决方案:
ind=[i for i,d in enumerate(l) if 'id' in d]
slices=[l[a:b] for (a,b) in zip(ind,ind[1:]+[len(l)])]