How to "combine" one iterable, based on some criteria?

I have a list of dictionaries that I want to make circular sort.

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

I want this result:

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

As I decided, it looks like this:

 def roundrobin(*iterables): # took from here https://docs.python.org/3/library/itertools.html#itertools-recipes "roundrobin('ABC', 'D', 'EF') --> ADEBFC" # 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))) 

Using Python defaultdict to separate items by source, and then using the roundrobin solution I got from Python docs.

But the solution does not apply to all cases, for example t, p, g = items_by_sources.values() will break when one source is missing or a new source is added.

How can I make a solution to cover more edge cases and make a pythonic solution?

+5
source share
1 answer

Here's a solution using itertools.groupby() splits your input into the appropriate groups:

 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) 

Due to how groupby() works, the clever use of iterators in the roundrobin() version that you took from the documentation is not very useful, so I rewrote it in a way that we hope is simpler:

  • Group iterability with key

  • While you still have groups:

    • Get into the first group from the list of groups

    • Place the first item from this group and give it.

    • If the group still has items, add them back to the end of the list.

Here it is in action:

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

The version of grouprobin() above assumes your list is already sorted. If not, you will need to sort it before grouping it:

 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) 
+2
source

Source: https://habr.com/ru/post/1264952/


All Articles