-1

I have the following toy function:

def foo(a):
    return [a+5]

And I am running the following code:

my_lst = [foo(x) for x in range(10) if x%2 == 0]

Getting:

[[5], [7], [9], [11], [13]]

But need:

[5,7,9,11,13]

But I want to get a plain list, not a list of lists. How can I do it without itertools.chain.from_iterable(my_lst) but with list comprehension? What is the best practice? itertools or list comprehension in this case?

Please advice.

I have tried:

[j[0] for j in [foo(x) for x in range(10) if x%2 == 0]] 

Should I do it like this or there is a better way?

SteveS
  • 3,789
  • 5
  • 30
  • 64

3 Answers3

1

With list comprehension, it's done using two for loops:

my_lst = [subx for x in range(10) if x%2 == 0 for subx in foo(x)]

Also if you have a list of lists of one elements, you can "transpose" after the fact the list using zip:

bad_lst = [foo(x) for x in range(10) if x%2 == 0]
[my_lst] = zip(*bad_lst)

Or you can "transpose" using a list comprehension combined with list unpacking as well if you truly want a list as output and not a tuple:

bad_lst = [foo(x) for x in range(10) if x%2 == 0]
my_lst = [x for [x] in bad_lst]
Lærne
  • 3,010
  • 1
  • 22
  • 33
  • 1
    itertools.chain is probably more efficient though. – Lærne Sep 14 '22 at 10:53
  • Please explain the trick with zip and what is transpose here? – SteveS Sep 14 '22 at 10:58
  • And what does the [my_lst] means? @Laerne – SteveS Sep 14 '22 at 11:03
  • 1
    Zip takes a tuple of sequences and return a sequence of tuple. E.g. `list(zip([0,1,2], ['a', 'b', 'c']))` will be `[(0, 'a'), (1, 'b'), (2, 'c')]`. In other words, zip converts `n` sequences of length `m` to `m` sequences of length `n`. You have 2D list `bad_lst` of size `n`×`1`. Using the `*` notation (see https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists), you pass along all the `n` sequences of `bad_lst` with length `1` to zip, which will spit out `1` sequence of length `n` with every elements. – Lærne Sep 14 '22 at 11:06
  • 1
    `[my_lst]` is list unpacking: If on the right hand side you have a list with one element `e`, this will set `my_lst` to e. If the list has 0 or more than 2 elements, this will fails. Likewise, if the right hand side had 3 elements, you could write `[e1, e2, e3] = right_hand_side` and have `e1` the first element, `e2` the second, `e3` the third. – Lærne Sep 14 '22 at 11:07
  • So in this case we have a list with one element - tuple created by ```zip```. So we transfer this tuple to ```my_lst``` that makes it a list? @laerne – SteveS Sep 14 '22 at 11:25
  • 1
    If you really want a list, not a tuple, you can use `my_lst = list(zip(*bad_lst)[0])`, but it begins to be somewhat messy to read: use a list comprehension then, I've added it to the answer. – Lærne Sep 15 '22 at 13:30
1

alternatively to Laernes answer, you can also use itertools as :

list(itertools.chain(*[foo(x) for x in range(10) if x%2 == 0]))

or

list(itertools.chain.from_iterable([foo(x) for x in range(10) if x%2 == 0]))

More options here:

Bijay Regmi
  • 1,187
  • 2
  • 11
  • 25
-1

Your function should return a number, not a list:

def foo(a):
    return a+5
Chris Heithoff
  • 1,787
  • 1
  • 5
  • 14
  • My function is just an example. In my program I have a lot of functions and the result is list of lists I want to flatten. – SteveS Sep 14 '22 at 10:46
  • 1
    Ok, I understand now. Your list comprehension `[j[0] for j in [foo(x) for x in range(10) if x%2 == 0]] ` works fine. I see no reason to prefer something else. – Chris Heithoff Sep 14 '22 at 10:48