Quantcast
Channel: How can I partition (split up, divide) a list based on a condition? - Stack Overflow
Viewing all articles
Browse latest Browse all 41

Answer by thehappycheese for How can I partition (split up, divide) a list based on a condition?

$
0
0

The previous answers don't seem to satisfy all four of my obsessive compulsive ticks:

  1. Be as lazy as possible,
  2. Evaluate the original Iterable only once
  3. Evaluate the predicate only once per item
  4. Provide nice type annotations (for python 3.7)

My solution isn't pretty, and I don't think I can recommend using it, but here it is:

def iter_split_on_predicate(predicate: Callable[[T], bool], iterable: Iterable[T]) -> Tuple[Iterator[T], Iterator[T]]:    deque_predicate_true = deque()    deque_predicate_false = deque()    # define a generator function to consume the input iterable    # the Predicate is evaluated once per item, added to the appropriate deque, and the predicate result it yielded     def shared_generator(definitely_an_iterator):        for item in definitely_an_iterator:            print("Evaluate predicate.")            if predicate(item):                deque_predicate_true.appendleft(item)                yield True            else:                deque_predicate_false.appendleft(item)                yield False    # consume input iterable only once,    # converting to an iterator with the iter() function if necessary. Probably this conversion is unnecessary    shared_gen = shared_generator(        iterable if isinstance(iterable, collections.abc.Iterator) else iter(iterable)    )    # define a generator function for each predicate outcome and queue    def iter_for(predicate_value, hold_queue):        def consume_shared_generator_until_hold_queue_contains_something():            if not hold_queue:                try:                    while next(shared_gen) != predicate_value:                        pass                except:                    pass        consume_shared_generator_until_hold_queue_contains_something()        while hold_queue:            print("Yield where predicate is "+str(predicate_value))            yield hold_queue.pop()            consume_shared_generator_until_hold_queue_contains_something()    # return a tuple of two generators      return iter_for(predicate_value=True, hold_queue=deque_predicate_true), iter_for(predicate_value=False, hold_queue=deque_predicate_false)

Testing with the following we get the output below from the print statements:

t,f = iter_split_on_predicate(lambda item:item>=10,[1,2,3,10,11,12,4,5,6,13,14,15])print(list(zip(t,f)))# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# Evaluate predicate.# Yield where predicate is True# Yield where predicate is False# [(10, 1), (11, 2), (12, 3), (13, 4), (14, 5), (15, 6)]

Viewing all articles
Browse latest Browse all 41

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>