2017-03-02 16 views
5

Tôi có một danh sách các từ điển, mà tôi muốn sắp xếp vòng xoay.Làm thế nào để "round-robin" một lần lặp lại dựa trên một số tiêu chí?

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

Tôi muốn kết quả này:

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

Con đường tôi giải quyết nó như sau:

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

Sử dụng Python của defaultdict để tách các mặt hàng theo nguồn và sau đó sử dụng các giải pháp roundrobin mà tôi nhận được từ tài liệu của Python.

Nhưng, giải pháp, không bao gồm tất cả các trường hợp, ví dụ t, p, g = items_by_sources.values() sẽ ngắt khi một nguồn bị thiếu hoặc nguồn mới đã được thêm.

Làm cách nào để tôi có thể đưa ra giải pháp để bao quát thêm các trường hợp cạnh và làm cho dung dịch trở thành pythonic?

Trả lời

2

Dưới đây là một giải pháp sử dụng itertools.groupby() chia đầu vào của bạn thành các nhóm thích hợp:

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) 

Do cách groupby() công trình, việc sử dụng thông minh của vòng lặp trong các phiên bản của roundrobin() bạn lấy từ các tài liệu không phải là rất hữu ích, vì vậy tôi đã viết lại nó trong một cách đó là hy vọng dễ dàng hơn để làm theo:

  1. Tập đoàn iterable bởi key

  2. Trong khi bạn vẫn còn có bất kỳ nhóm còn lại:

    1. Pop nhóm đầu tiên từ phía trước của danh sách của các nhóm

    2. Pop mục đầu tiên từ nhóm đó, và mang nó.

    3. Nếu vẫn còn các mục trong nhóm, hãy thêm lại vào cuối danh sách.

Đây là nó trong hành động:

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

Các phiên bản của grouprobin() trên giả định rằng danh sách của bạn đã được sắp xếp. Nếu không, nó sẽ cần được sắp xếp trước khi được nhóm:

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) 
Các vấn đề liên quan