3

I would like to turn this code into a list comprehension:

l = list()
for i in range(10):
    j = fun(i)
    if j:
        l.append(j)

Meaning that I'd like to add only truthy fun() result values to the list. Without the truthy check of that function call, the list comprehension would be:

l = [fun(i) for i in range(10)]

Adding a if fun(i) to the list comprehension would cause two evaluations of fun() per iteration (actually, not always it seems!), thus causing unintended side effects if fun() is not pure.

Can I capture the result of fun(i) and use it in that same comprehension, essentially adding the if j? (Related question here)

Community
  • 1
  • 1
Jens
  • 8,423
  • 9
  • 58
  • 78
  • 1
    `l = filter(None, map(fun, range(10)))` or `l = [x for x in (fun(i) for i in range(10)) if x]` – falsetru Mar 15 '16 at 05:23
  • @falsetru: Your first statement returns an iterable `filter` object and not a list, whereas your second statement is a [filter idiom](https://docs.python.org/3.4/library/functions.html#filter) idiom and an actual list. – Jens Mar 26 '16 at 13:31
  • Yup, you need to wrap the first one with `list(...)` to get a list. – falsetru Mar 26 '16 at 14:06
  • @falsetru: And that would result in a total of two iterations, unless all [`map`](https://docs.python.org/3.4/library/functions.html#map), [`filter`](https://docs.python.org/3.4/library/functions.html#filter) and [`list`](https://docs.python.org/3.4/library/functions.html#func-list) operate lazily (on demand). – Jens Mar 26 '16 at 16:02
  • `map`, `filter` operate lazily in Python 3.x (returns iterators) – falsetru Mar 26 '16 at 23:29

3 Answers3

3

You can make an inner generator in the list comp so you will look over the results of func

l = [j for j in (func(i) for i in range(10)) if j]
Yoav Glazner
  • 7,936
  • 1
  • 19
  • 36
  • This is the [filter idiom](https://docs.python.org/3.4/library/functions.html#filter), but unlike the idiom which returns an iterable `filter` object, `l` is the requested list. – Jens Mar 26 '16 at 13:27
0

Or combining two suggested solutions:

filter(None, (func(i) for i in range(10)))
Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159
  • This returns a `filter object` iterable, and not a list per se. In most cases, that might not matter though. – Jens Mar 15 '16 at 12:12
0

Edit

Much simpler:

[res for res in map(func, range(10)) if res]

Thanks for the hint to falstru.

Old answer

Another option would to use a helper generator function:

def call_iter(func, iterable):
    for arg in iterable:
        yield func(arg)

[res for res in call_iter(func, range(10)) if res]
Mike Müller
  • 82,630
  • 20
  • 166
  • 161