Python: split a list based on a condition?

前端 未结 30 2508
误落风尘
误落风尘 2020-11-22 06:56

What\'s the best way, both aesthetically and from a performance perspective, to split a list of items into multiple lists based on a conditional? The equivalent of:

30条回答
  •  我寻月下人不归
    2020-11-22 07:02

    Here's the lazy iterator approach:

    from itertools import tee
    
    def split_on_condition(seq, condition):
        l1, l2 = tee((condition(item), item) for item in seq)
        return (i for p, i in l1 if p), (i for p, i in l2 if not p)
    

    It evaluates the condition once per item and returns two generators, first yielding values from the sequence where the condition is true, the other where it's false.

    Because it's lazy you can use it on any iterator, even an infinite one:

    from itertools import count, islice
    
    def is_prime(n):
        return n > 1 and all(n % i for i in xrange(2, n))
    
    primes, not_primes = split_on_condition(count(), is_prime)
    print("First 10 primes", list(islice(primes, 10)))
    print("First 10 non-primes", list(islice(not_primes, 10)))
    

    Usually though the non-lazy list returning approach is better:

    def split_on_condition(seq, condition):
        a, b = [], []
        for item in seq:
            (a if condition(item) else b).append(item)
        return a, b
    

    Edit: For your more specific usecase of splitting items into different lists by some key, heres a generic function that does that:

    DROP_VALUE = lambda _:_
    def split_by_key(seq, resultmapping, keyfunc, default=DROP_VALUE):
        """Split a sequence into lists based on a key function.
    
            seq - input sequence
            resultmapping - a dictionary that maps from target lists to keys that go to that list
            keyfunc - function to calculate the key of an input value
            default - the target where items that don't have a corresponding key go, by default they are dropped
        """
        result_lists = dict((key, []) for key in resultmapping)
        appenders = dict((key, result_lists[target].append) for target, keys in resultmapping.items() for key in keys)
    
        if default is not DROP_VALUE:
            result_lists.setdefault(default, [])
            default_action = result_lists[default].append
        else:
            default_action = DROP_VALUE
    
        for item in seq:
            appenders.get(keyfunc(item), default_action)(item)
    
        return result_lists
    

    Usage:

    def file_extension(f):
        return f[2].lower()
    
    split_files = split_by_key(files, {'images': IMAGE_TYPES}, keyfunc=file_extension, default='anims')
    print split_files['images']
    print split_files['anims']
    

提交回复
热议问题