如何根据某些标准"循环"单个迭代?

Ale*_*enz 5 python sorting algorithm data-structures python-3.x

我有一个字典列表,我想循环排序.

sample = [
    {'source': 'G', '"serial"': '0'},
    {'source': 'G', '"serial"': '1'},
    {'source': 'G', '"serial"': '2'},
    {'source': 'P', '"serial"': '30'},
    {'source': 'P', '"serial"': '0'},
    {'source': 'P', '"serial"': '1'},
    {'source': 'P', '"serial"': '2'},
    {'source': 'P', '"serial"': '3'},
    {'source': 'T', '"serial"': '2'},
    {'source': 'T', '"serial"': '3'}
]
Run Code Online (Sandbox Code Playgroud)

我想要这个结果:

sample_solved = [
    {'source': 'G', '"serial"': '0'},
    {'source': 'P', '"serial"': '30'},
    {'source': 'T', '"serial"': '2'},
    {'source': 'G', '"serial"': '1'},
    {'source': 'P', '"serial"': '1'},
    {'source': 'T', '"serial"': '3'},
    {'source': 'G', '"serial"': '2'},
    {'source': 'P', '"serial"': '0'},
    {'source': 'P', '"serial"': '2'},
    {'source': 'P', '"serial"': '3'}
]
Run Code Online (Sandbox Code Playgroud)

我解决它的方式如下:

def roundrobin(*iterables):
    # took from here https://docs.python.org/3/library/itertools.html#itertools-recipes

    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis

    pending = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

def solve():
    items_by_sources = collections.defaultdict(list)

    for item in sample2:
         items_by_sources[item["source"]].append(item)

    t, p, g = items_by_sources.values()

    print(list(roundrobin(t, p, g)))
Run Code Online (Sandbox Code Playgroud)

使用Python defaultdict按源分离项目,然后使用我从Python的文档中获得的roundrobin解决方案.

但是,解决方案并未涵盖所有情况,例如,t, p, g = items_by_sources.values()当缺少一个源或添加新源时会中断.

如何制定解决方案以覆盖更多边缘情况并使解决方案成为pythonic?

Zer*_*eus 2

这是一个用于itertools.groupby()将您的输入分成适当组的解决方案:

from itertools import groupby

def grouprobin(iterable, key):
    groups = [list(g) for k, g in groupby(iterable, key)]
    while groups:
        group = groups.pop(0)
        yield group.pop(0)
        if group:
            groups.append(group)
Run Code Online (Sandbox Code Playgroud)

由于这种方式groupby()有效,在您从文档中获取的版本中巧妙地使用迭代器roundrobin()并不是很有帮助,因此我以一种希望更容易遵循的方式重写了它:

  1. 将可迭代对象分组为key

  2. 当您仍然有任何组时:

    1. 从组列表的前面弹出第一个组

    2. 从该组中弹出第一项,然后生成它。

    3. 如果组中仍有项目,请将其追加到列表末尾。

这是在行动中:

>>> sample_solved = list(grouprobin(sample, key=lambda d: d['source']))
>>> from pprint import pprint
>>> pprint(sample_solved)
[{'"serial"': '0', 'source': 'G'},
 {'"serial"': '30', 'source': 'P'},
 {'"serial"': '2', 'source': 'T'},
 {'"serial"': '1', 'source': 'G'},
 {'"serial"': '0', 'source': 'P'},
 {'"serial"': '3', 'source': 'T'},
 {'"serial"': '2', 'source': 'G'},
 {'"serial"': '1', 'source': 'P'},
 {'"serial"': '2', 'source': 'P'},
 {'"serial"': '3', 'source': 'P'}]
Run Code Online (Sandbox Code Playgroud)

上面的版本grouprobin()假设您的列表已经排序。如果没有,则需要在分组之前对其进行排序:

def grouprobin(iterable, key):
    groups = [list(g) for k, g in groupby(sorted(iterable, key=key), key)]
    while groups:
        group = groups.pop(0)
        yield group.pop(0)
        if group:
            groups.append(group)
Run Code Online (Sandbox Code Playgroud)