8

Is there a way to write an If (or equivalent) statement that can have many arguments, and if any of those satisfy the logic, use that variable?

For instance

if len(x) == 1 or len(y) == 1 or len(z) == 1 or ... len(zz) == 1:
    # do something with the variable that met the condition

So say only z has length 1, could I write above idea/formula in a way that takes the first True answer and use that?

So something like

x = "123"
y = "234"
z = "2"
xx = "1234"
yy = "12345"

if len(x) == 1 or len(y) == 1 or len(z) == 1 or len(xx) == 1 or len(yy) == 1:
    #do something with the variable that satisfies the condition, so `z` in this case.

Does that make any sense? The variables' lengths could change any time, so I'd like to be able to say "If any of the conditions are met, use the variable that met the condition"...?

In the above, I don't know beforehand that zwill be the only one to meet the criteria, so my Then statement can't be z = "new value" or whatever I want to do with it.

Edit: Sorry, per comments I know checking for len on integers isn't okay. This is solely for illustration purposes and it was the first thing I thought of to "test". Sorry if the len bit is confusing. I'm mainly just trying to see if I can use If statements (or related ones) where I don't know which of my many variables will meet a condition. (I'm still new regarding python, so my sincere apologies for my lack of semantics or proper terms). I'd like to avoid elif if at all possible just because it can get stringy. (But if that's the most pythonic way, then so be it!)

BruceWayne
  • 22,923
  • 15
  • 65
  • 110
  • @OlivierMelançon - Ah, good call. I just tried to think of something quick here. The actual use case is strings. (I'll edit) – BruceWayne Aug 10 '18 at 19:30
  • 1
    Is it guaranteed that only one value will have a length on 1? Otherwise, do you always want the first value with the condition to be used, or are there additional criteria? – Triggernometry Aug 10 '18 at 19:30
  • 1
    Have you tried using multiple `elif` statements? – brandonwang Aug 10 '18 at 19:30
  • 2
    This is effectively the same question as [Get the first item from an iterable that matches a condition](//stackoverflow.com/q/2361426). – Aran-Fey Aug 10 '18 at 19:32
  • 2
    Possible duplicate of [Get the first item from an iterable that matches a condition](https://stackoverflow.com/questions/2361426/get-the-first-item-from-an-iterable-that-matches-a-condition) – David Zemens Aug 10 '18 at 19:33

4 Answers4

11

While @pault 's answer addresses your question, I think it isn't super readable. If you have a couple of variables only, pythons mantra dictate a straightforward, explicit way:

if len(x) == 1:
  f(x)
elif len(y) == 1:
  f(y)
elif len(z) == 1:
  f(z)

Otherwise, if you have a list, a for loop is readable and efficient:

for l in ls:
    if len(l) == 1:
        f(l)
        break
wim
  • 338,267
  • 99
  • 616
  • 750
erenon
  • 18,838
  • 2
  • 61
  • 93
10

You could use next here to pick the first item out of a list of options that meets your criteria:

value = next((item for item in [x, y, z] if len(item)==1), None)
if value is not None:
    ...

The second argument to next() is the default value if no values meet your criteria.

pault
  • 41,343
  • 15
  • 107
  • 149
  • I get the error: `TypeError: next() takes no keyword arguments` using `value = next((item for item in [x, y, z] if item=="no"), default=None)` – BruceWayne Aug 14 '18 at 14:05
  • @BruceWayne you're right. I just double checked the [docs](https://docs.python.org/3/library/functions.html#next) and the second arg is not a keyword arg. Try `value = next((item for item in [x, y, z] if item=="no"), None)` (remove the `default=`) – pault Aug 14 '18 at 14:33
4

What you describe has a general implementation called first_true in the itertools recipes.

def first_true(iterable, default=False, pred=None):
    """Returns the first true value in the iterable.

    If no true value is found, returns *default*

    If *pred* is not None, returns the first item
    for which pred(item) is true.

    """
    # first_true([a,b,c], x) --> a or b or c or x
    # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
    return next(filter(pred, iterable), default)

Example

value = first_true([x, y, z], pred=lambda x: len(x) == 1)

if value:
    ...
Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
1

A small list comprehension would suffice:

passed = [i for i in (x, y, z, xx, yy) if len(i) == 1]
if passed:
     # ... use the ones that passed, or 'passed[0]' for the first item
N Chauhan
  • 3,407
  • 2
  • 7
  • 21