Advanced Nested List Comprehension Syntax

Advanced Nested List Comprehension Syntax

you need to use some parentheses:

((x for x in range(10) if x%2==i) for i in range(2))

This didn't make sense to me, so I
thought it best to try something
simpler first. So I went back to lists
and tried:

[>>> [x for x in range(10) if x%2==i for i in range(2)]
[1, 1, 3, 3, 5, 5, 7, 7, 9, 9]

That worked because a previous list comprehension leaks the i variable to the enclosing scope, and become the i for the current one. Try starting a fresh python interpreter, and that would fail due to NameError. The counter's leaking behavior has been removed in Python 3.

EDIT:

The equivalent for loop for:

(x for x in range(10) if x%2==i for i in range(2))

would be:

l = []
for x in range(10):
if x%2 == i:
for i in range(2):
l.append(x)

which also gives a name error.

EDIT2:

the parenthesed version:

((x for x in range(10) if x%2==i) for i in range(2))

is equivalent to:

li = []
for i in range(2):
lx = []
for x in range(10):
if x%2==i:
lx.append(x)
li.append(lx)

Nested if / else clauses in List Comprehension Syntax Error

This will read much more easily as a regular for loop:

new_list = []
for i in old_list:
if not re.findall(r'find_pattern', i, re.M):
new_list.append(re.sub(r'replace_a', 'with_b', i))
elif foo == re.match("pattern", foo):
new_list.append(re.sub(r'replace_c', r'with_d', i))
# else:
# i

Your problem is that a conditional expression must always take an else clause, because it has to have some value whether or not the condition is true. With an if statement, however, you can omit the else. The above, for instance, has the ability to make new_list shorter than old_list, as not every i needs to result in a call to new_list.append. Uncommenting the else results in the same result as jpp's answer.

If you insist on using a list comprehension, then you can format it to make it more readable. Consider

new_list = [re.sub(r'replace_pattern_a', 'with_pattern_b', i)
if not re.findall(r'find_pattern', i, re.M) else
re.sub(r'replace_pattern_c', r'with_pattern_d', i)
if foo == re.match("pattern", foo) else
i
for i in old_list]

although the conditional expression really isn't designed for such nesting. This visually separates the expressions that could be added to the new list from the conditions used to make the decision, but I'm not a big fan. There are other ways to format that make different trade-offs, but IMO the regular for loop is superior.


As jpp mentions, another way to refactor is to define a generator function:

def foo(old_list):
for i in old_list:
if not re.findall(r'find_pattern', i, re.M):
yield re.sub(r'replace_a', 'with_b', i))
elif foo == re.match("pattern", foo):
yield re.sub(r'replace_c', r'with_d', i))
else:
yield i

new_list = list(foo())

which would have its pros and cons as well. (I think enumerating those is probably beyond the scope of this answer, but it does fall somewhere between the two extremes of a single list comprehension and an explicit for loop. It also comes closest to the construct I miss from my Perl days, the do statement, which is something like a lambda that doesn't take any arguments, but can contain arbitrary statements.)

Nested list comprehension to flatten nested list

Your syntax was a little wrong. Try below snippet.

nested_list = [[1,2,3], [4,5,6], [7,8,9]]
odds_evens = ['odd' if n % 2 != 0 else 'even' for l in nested_list for n in l]
print(odds_evens)

Output:

['odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']

Nested List Comprehension

Double Iteration in List Comprehension

Gee, I guess I found the anwser: I was not taking care enough about which loop is inner and which is outer. The list comprehension should be like:

[x for b in a for x in b]

to get the desired result, and yes, one current value can be the iterator for the next loop.

Correct syntax for nested list or set comprehension

tags = set(tag  for e in my_obj['Episodes'] for tag in e['Tags'])

you need to change the order and get e first... :)

Create a list within a list comprehension

I am guessing you are looking for -

[[char for i,char in enumerate(line) if len(line[i:])>3] for line in grid]

You should move the second for loop and condition inside the list, not the first one. When there were no lists, the order of execution was - first for loop - for line in grid -> second for loop - for i,char in enumerate(line) .

The above would preserve that order, and create chars for each line meeting your condition as a separate list.

Python: filtering in nested list comprehension

Other than doing it in one line:

[(i, j) for i, row in enumerate(matrix) for j, el in enumerate(row) if el == 'W']
#[(0, 1), (1, 1), (2, 0)]

You can also use numpy:

import numpy as np
list(zip(*np.where(np.array(matrix)=='W')))
#[(0, 1), (1, 1), (2, 0)]

Python Nested List Comprehension with If Else

[str.replace(i[0],k,header_replace[k]) if k in i[0] for k in header_replace.keys() for i in cursor.description]

this is a SyntaxError, because if expressions must contain the else part. You probably meant:

[i[0].replace(k, header_replace[k]) for k in header_replace for i in cursor.description if k in i[0]]

With the if at the end. However I must say that list-comprehension with nested loops aren't usually the way to go.
I would use the expanded for loop. In fact I'd improve it removing the replaced flag:

headers = []
for header in cursor.description:
for key, repl in header_replace.items():
if key in header[0]:
headers.append(header[0].replace(key, repl))
break
else:
headers.append(header[0])

The else of the for loop is executed when no break is triggered during the iterations.


I don't understand why in your code you use str.replace(string, substring, replacement) instead of string.replace(substring, replacement). Strings have instance methods, so you them as such and not as if they were static methods of the class.

Nested list comprehension dict and list python

I think you need something like following (if data is your "list of dicts"):

[d.get('price') for d in data]

What I am doing is, iterating over list of dicts and to each dict use get('price') (as get() doesn't throw key exception) to read value of 'price' key.

Note: Avoid to use 'dict' as a variable name as it is in-build type name.

Example:

>>> data = [ {'price': 2, 'b': 20}, 
{'price': 4, 'a': 20},
{'c': 20}, {'price': 6, 'r': 20} ] # indented by hand
>>> [d.get('price') for d in data]
[2, 4, None, 6]
>>>

You can remove None in output list, by adding explicit if-check as: [d['price'] for d in data if 'price' in d].

Reviews on your code:

[b for a:b in dict.items() if a='price' for dict in data]
  1. You have silly syntax error a:b should be a, b

  2. Second, syntax error in if condition — a='price' should be a == 'price' (misspell == operator as =)

  3. Order of nested loops are wrong (in list compression we write nested loop later)

  4. This is it not an error, it is but bad practice to use inbuilt type name as variable name. You shouldn't use 'dict', 'list', 'str', etc as variable(or function) name.

    Correct form of your code is:

      [b for dict in data for a, b in dict.items() if a == 'price' ]
  5. In your list compression expression for a, b in dict.items() if a == 'price' loop is unnecessary—simple get(key), setdefualt(key) or [key] works faster without loop.



Related Topics



Leave a reply



Submit