在分隔符上拆分列表-将分隔符保留在组中



我有一个奇怪的数据结构,它是由我无法控制的外部服务返回给我的。

数据本质上是一个字典列表,但以一种奇怪的方式分割:它作为字典列表返回,其中每个字典都有一个单个键。从这个列表中提取多个元素会生成字典中的所有键。

代码中:

[ {'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)])]

最新更新