'Python transform and filter list with for / if

Is there a way to both transform and filter in a single list comprehension, i.e.:

def transform(el):
    if some_condition(el):
        return None

    return complex_logic(el)

def main():
    transformed = [transform(el) for el in some_list if transform(el) != None]

but avoid calling transform twice? I.e. assign it to a variable, something like (in pseudo-Python):

def main():
  transformed = [transformed for el in some_list let transformed = transform(el) if transformed != None]


Solution 1:[1]

Since Python 3.8 you can use walrus operator :=:

def main():
    return [res for el in some_list if (res := transform(el)) is not None]

This way the result of calling to the transform function is stored in res then you can use it in the expression part of your list comprehension.

Solution 2:[2]

Replace let transformed = transform(el) with for transformed in [transform(el)].

Solution 3:[3]

I would approach the solution from simple, over idiomatic to readable:

Simple loop with temp var

The simple but verbose for-loop can be used to "cache" the transformation result in a temporary variable t:

def transform(el):
    if some_condition(el):
        return None
    return complex_logic(el)


def main():
    transformed_list = []
    for el in some_list:
        t = transform(el)  # invoked once
        if t is not None:  # equivalent to `if transform(el) != None`
            transformed_list.append(t)

Embedded List Comprehension

Like Kelly Bundy suggests, embed the list-comprehensions:

  1. transformation of elements
  2. filter for non-null

See also Temporary variable within list comprehension

Decouple condition from transformation

Command–query separation (CQS) can be applied

to have a simplifying effect on a program, making its states (via queries) and state changes (via commands) more comprehensible.

Assume the 2 given functions (some_condition and complex_logic) were separately defined because each implements a single-responsibility (SRP). Then it would be consequential to take advantage of this separation and reuse the 2 steps in suitable composition:

  1. Query: by filter first using the condition function as predicate
  2. Command: afterwards to transform by complex logic

This way the pipeline or stream might even become more readable:

transformed = [complex_logic(el) for el in filter(some_condition, some_list)]

Finally this is close to what Samwise advised in his comment: Now the SRP is followed.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 hc_dev
Solution 2 Kelly Bundy
Solution 3