【问题标题】:How to "round-robin" a single iterable based on some criteria?如何根据某些标准“循环”单个迭代?
【发布时间】:2017-07-22 11:21:08
【问题描述】:

我有一个字典列表,我想对其进行循环排序。

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 文档中获得的循环解决方案。

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

如何制定解决方案以涵盖更多边缘情况并使解决方案 Python 化?

【问题讨论】:

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


    【解决方案1】:

    这是一个使用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. 如果组中仍有项目,请将其追加到列表末尾。

    它在行动:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多