planetp
planetp

Reputation: 16065

What is a pythonic way to conditionally compose a sequence?

In Perl I can do something like this to dynamically create an array based on some conditions:

my @arr = (  
    $foo_defined ? 'foo' : (),    
    $bar_defined ? 'bar' : (),
    $baz_defined ? 'baz' : (),             
);

This creates an array of up to 3 elements based on the values of the variables. What would be the closest alternative to this in Python?

Upvotes: 4

Views: 91

Answers (4)

Kelly Bundy
Kelly Bundy

Reputation: 27588

Just one more way:

arr = [
    *(['foo'] if foo_defined else ()),
    *(['bar'] if bar_defined else ()),
    *(['baz'] if baz_defined else ()),
    ]

Upvotes: 0

chepner
chepner

Reputation: 531035

A close literal translation might be

arr = list(chain(
    ['foo'] if foo_defined else [],
    ['bar'] if bar_defined else [],
    ['baz'] if baz_defined else [],
    ))

a if b else c is the Python equivalent of b ? a : c. chain concatenates multiple iterable values into a single sequence, similar to how ( (), (), () ) builds an array consisting of array elements (rather than nesting the arrays). list turns the chain object into an actual list.

The differing semantics of a Perl array and a Python list make it difficult to do exactly the same thing as tersely as Perl does. Python lacks the ability to have an expression evaluate to nothing to simulate the Perl array handling in your example. (Or put another way, [ [], [], [] ] in Python creates a list containing 3 empty lists, rather than a single empty list.)


Patrick Artner's answer takes advantage of the fact that a list comprehension can selectively include values from one iterable into the list under construction.

A combination of his and my answers might look like

arr = [s for s in ['foo' if foo_defined else None,
                   'bar' if bar_defined else None,
                   'baz' if baz_defined else None] if s is not None]

The difference, though, is that a full list is built first, then a second, final list is built from the first one.


Rolv Apneseth's answer dispenses with a single expression, building the result from an initially empty list and conditionally adding values to it one item at a time. It's terse, but the simulation of Perl-style statement modifiers isn't really idiomatic in Python. Instead, one would just use if statements:

arr = []
if foo_defined:
    arr.append("foo")
if bar_defined:
    arr.append("bar")
if baz_defined:
    arr.append("baz")

Upvotes: 4

Patrick Artner
Patrick Artner

Reputation: 51643

Conditional list comprehension can do it:

defines = [True, False, True]
data =    ["foo", "bar", "baz"]


result = [d for defined,d in zip(defines,data) if defined]

print(result)

to get

['foo', 'baz']

zip() needs same-lenght lists - use iterools.zip_longest if you need some more leeway or defaults.

Upvotes: 2

Rolv Apneseth
Rolv Apneseth

Reputation: 2118

If I understood correctly (I don't know any Perl I'm afraid), maybe something like this:

foo_defined = True
bar_defined = False
baz_defined = True


arr = []
foo_defined and arr.append("foo")
bar_defined and arr.append("bar")
baz_defined and arr.append("baz")

Output

>  print(arr)
['foo', 'baz']

Upvotes: 1

Related Questions