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

时间:2017-03-02 16:11:24

标签: python algorithm python-3.x sorting data-structures

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

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'}
]

我想要这个结果:

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'}
]

我解决它的方法如下:

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)))

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

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

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

1 个答案:

答案 0 :(得分: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)

由于groupby()的工作方式,在您从文档中获取的roundrobin()版本中巧妙使用迭代器并不是非常有用,因此我已将其重写为一种希望更容易理解的方式:

  1. 将可迭代分组为key

  2. 虽然您还剩下任何群组:

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

    2. 弹出该组中的第一个项目,并将其合并。

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

  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'}]
    

    上面的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)