0

So this is a normal any() method checking if the iterable returns True in any of its elements:

list_a = ['test', 'hello']
list_b = ['test', 'bye']
name = 'test'
any(name == item for item in list_a)
True

But what about combining two iterables?

This works:

name = 'hello'
any(name == item_a for item_a in list_a or name == item_b for item_b in list_b)
True

This doesnt:

name = 'bye'
 any(name == item_a for item_a in list_a or name == item_b for item_b in list_b)
False

This can be simplyfied to:

any([True] or [False]) vs any([False] or [True])

How can it be transformed to:

any([True, False]) or any([False, True])

Any way to combine those two iterators within the any() method?

lapinkoira
  • 8,320
  • 9
  • 51
  • 94

3 Answers3

1

Not sure what exactly you want, but this is simpler:

any(name == item for item in list_a + list_b)

take 2:

getting attribute value from objects:

any([any(name==item.get_value() for item in list1), any(name==item.obtain_value() for item in list2)])

It lost its simplification factor, so it doesn't really address your question.
It retains its lazy evaluation short circuiting though.

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
  • It could be two iterables with different fields names like any(name == item_a.field for item_a in list_a or name == item_b.name for item_b in list_b) – lapinkoira Nov 24 '17 at 11:39
  • Ah, in that case, it is indeed oversimplified. – Reblochon Masque Nov 24 '17 at 12:08
  • Pretty sure your take 2 doesn't work because `any` takes exactly one argument, so your outer `any` will raise. And if it worked your inner `any`s would kill the outer `any`s short-circuiting because all function arguments are evaluated before the function is called. – Paul Panzer Nov 24 '17 at 13:13
  • Good catch, I included the two arguments in a list instead. – Reblochon Masque Nov 24 '17 at 13:24
1

any accepts a single iterable. For simple lists, you could just do

any(['hello'=word for word in list1 + list2])

For other type of iterators, you can use itertools.chain to produce a single iterable from them.

In your case however, the simplest thing seems

if 'hello' in list1 + list2
blue_note
  • 27,712
  • 9
  • 72
  • 90
  • Maybe I oversimplified the answer using list of strings, it could be two iterables returning objects like any(name == item_a.field for item_a in list_a or name == item_b.name for item_b in list_b) – lapinkoira Nov 24 '17 at 11:41
  • I don't think there's a way to get `mylist.field` directly. You should either use something `map(getattr, ...)` or, better, a list comprehension. Either way, `any` accepts a single iterable, so you have to combine the two, either by `list1+list2`, or with `itertools.chain` – blue_note Nov 24 '17 at 11:49
1

Setting for a moment list_a to something with a False truth value helps reveal what's wrong with your first approach:

list_a = []
any(name == item_a for item_a in list_a or name == item_b for item_b in list_b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'item_b' is not defined

This should help convince you that the any line is interpreted like

any(name == item_a for item_a in (list_a or name == item_b) for item_b in list_b)

Because or short-circuits and list_a is True in your original example the or name == item_b has no effect there.

Your desired behavior can be achieved by oring two any statements or itertools.chaining the operands of any

import itertools as it
name = 'bye'
any(it.chain((name==item_a for item_a in list_a), (name==item_b for item_b in list_b)))
# True
Paul Panzer
  • 51,835
  • 3
  • 54
  • 99