Unexpected output in nested list comprehension in Python - python

I have a nested list comprehension, when I print the output, it gives me generator object, I was expecting a tuple.
vector = [[1,2],[2,3],[3,4]]
res = (x for y in vector for x in y if x%2 == 0)
print(res)
I thought since I have small bracket for res assignment, I thought the result will be tuple, but it gives me a generator object.
But when I use a traditional approach, it gives me a list.
lst = []
for y in vector:
print(y)
for x in y:
print(x)
if x%2 == 0:
lst.append(x)
print(lst)
It looks clear here that it gives a list there is no confusion here in the second one, but the first one is little confusing as why it gives a generator object.

Python doesn't know that you want your generator expression to be a list until you make it one. ;^)
vector = [[1,2],[2,3],[3,4]]
res = list((x for y in vector for x in y if x%2 == 0)) # use generator expression to make a list
print(res)
Output:
[2, 2, 4]

Related

Basic Python manipulation of nested list

For the following nested list, Lst, I need to keep the first inner list, square the second, and cube the last one.
Lst = [[1,2,3],[2,3,4],[3,4,5]]
My current code is squaring all the nested list in the Lst.
list(map(lambda lst: list(map(lambda x: x**2, lst)), Lst))
How can I fix this? I just started learning Python.
Since you're not doing the same operation on each nested list, you shouldn't use map() for the top-level list. Just make a list of results of different mappings for each.
[Lst[0], list(map(lambda x: x**2, lst[1])), list(map(lambda x: x**3, lst[2]))]
However, there's an obvious pattern to this, so you can generalize it using enumerate() and a list comprehension:
[list(map(lambda x: x**i, sublist)) for i, sublist in enumerate(Lst, 1)]
[list(map(lambda x: x**i, l)) for i,l in enumerate(Lst, 1)]
[[1, 2, 3], [4, 9, 16], [27, 64, 125]]
Your processing obviously needs to take into account where in the list you are. Forcing this into a one-liner makes it more dense than it really needs to be, but see if you can follow along with pencil and paper.
[[x if i == 0 else x**2 if i == 1 else x**3 for x in Lst[i]] for i in range(3)]
Demo: https://ideone.com/o8y0ta
... Or, as cleverly suggested in Barmar's answer,
[[x**i for x in Lst[i]] for i in range(3)]
Try indexing the outer list and then call the map function on each index
def square(number) :
return number ** 2
def cube(number) :
return number ** 3
lst = [[1,2,3],[2,3,4],[3,4,5]]
lst[0] = lst[0]
lst[1] = list(map(square, lst[1]))
lst[2] = list(map(cube, lst[2]))
print(lst)

python for loop with lists

I get:
if(lst[i]%2!=0):
IndexError: list index out of range
from this code:
lst=[1,2,3,4,5,6]
for i in range(len(lst)):
if(lst[i]%2!=0):
lst.remove(lst[i])
print(lst)
I am trying to print only the even numbers in a list and I do not see a problem with my code
why am I getting this error ?
You should not remove from a list while iterating it. Use, for instance, a list comprehension and slice assignment to achieve the same modification:
lst = [1,2,3,4,5,6]
lst[:] = [x for x in lst if x % 2 == 0]
# or simply (if you don't need to mutate the original list)
# lst = [x for x in lst if x % 2 == 0]
print(lst)
# [2, 4, 6]
This has also better time complexity (linear) whereas the repeated remove approach is quadratic.
Instead of removing the item from the original list, you can also split the original list into 2 new lists, evens and odds
lst=[1,2,3,4,5,6]
evens = []
odds = []
for i in lst):
if(i % 2 != 0):
odds.append(i)
else:
evens.append(i)
print(evens)

How do I keep the order of sorted numbers in a list the same in Python

I have a function in which:
Given a list of Python values xs and a non-negative integer n, construct and return a copy of xs but with each value replicated n times. Do NOT modify the original list 'xs'
To do this, I created some small code that multiplied the list xs and then sorted it.
def replicate(xs,n):
xscopy = sorted(n * xs)
return xscopy
This code will result in a function input like "replicate([1,2,2,3],2)" to output as [1,1,2,2,2,2,3,3] which is correct. Note how the correct output has the numbers' places matching.
However, when a negative number is in the list, the 'sort' function sees the negative number as smaller than the positive numbers and shifts the position of the negative number from where it was originally on the list.
For example: replicate([1,-1,2,1],2) outputs to [-1,-1,1,1,1,1,2,2] rather than, the correct version, [1,1,-1,-1,2,2,1,1].
Notice how the negative 1 shifted?
I am required to use a loop of some sort (while or for) for this task and I can't figure out how I could incorporate a loop that would both preserve the positions of the numbers and append the numbers properly into a new list (a new list that contains both the original xs, and the duplicates, in the same order as the original.
EDIT: I should add that list comprehension is restricted for this task
You could just use a nested list comprehension if you want to keep the order of the elements from the original list, this will take each element from the list xs, repeat it n times and then go for the next element and so on:
xs = [1,-1,2,1]
n = 2
[x for x in xs for _ in range(n)]
# [1, 1, -1, -1, 2, 2, 1, 1]
xs = [1,2,3]
n = 2
[x for x in xs for _ in range(n)]
# [1, 1, 2, 2, 3, 3]
def replicate(xs, n):
return [item for item in xs for repeat in range(n)]
You don't need to sort it. For each number in the list, loop n times and add it to a new list, so you preserve the original list and still get what you wanted.
def replicate(xs,n):
result = []
for num in xs:
for _ in range(n):
result.append(num)
return result
A cleaner way would be using list comprehension return [num for num in xs for _ in range(n)]
Either way, output of replicate([1,-1,2,1],2) is [1, 1, -1, -1, 2, 2, 1, 1]
You can create a single-element list, and then multiply it. Once you have an n-element list, you can extend() the result list with that portion of the result.
result = []
for item in xs:
nitems = [item] * n
result.extend(nitems)
The functools.reduce function can be used for this:
import functools
return functools.reduce((lambda a,b: a + [b]*n), xs, [])
The sum builtin can do something similar:
return sum( [ [x]*n for x in xs], [])
The itertools.chain function can paste together a bunch of iterables, so you could just multiply the values into a bunch of sub-lists and pass them to it:
import itertools
return list(itertools.chain(*[ [x]*n for x in xs ]))
Or, without the splat (*) operator:
import itertools
return list(itertools.chain.from_iterable([[x]*n for x in xs])
Or, you could zip the original list against itself, then flatten those tuples. (This would be good for long lists, especially if you could just return the iterable instead of a list):
import itertools
return list(itertools.chain.from_iterable(zip(*[iter(xs) for _ in range(n)])))

Python list recursive changes

I have a bug in my attempt to add to a list a sequence of numbers recursively. E.g. if the input is [5,3,9], I do [5+1,3+2,9+3] and output [6,5,12]. I want to do this recursively so the way I'm doing it is going through and adding one to a smaller and smaller part of the list as below:
def add_position_recur(lst, number_from=0):
length = len(lst)
# base case
if (length <= 1):
lst = [x+1 for x in lst]
print "last is", lst
else:
lst = [x+1 for x in lst]
print "current list is", lst
add_position_recur(lst[1:], number_from)
return lst
The problem, though, is that all this does is add 1 to every element of the list. Where is the bug? Is it to do with the way I return the list in the base case?
When you recurse down your call stack you slice lst which creates a new list, this is not the same as what you return, so you will only ever return the changes you've applied to your list in the first call to the function, losing all changes further down the stack:
>>> add_position_recur([1,2,3])
[2, 3, 4]
This should have returned [2, 4, 6].
You need to consider reassembling the list on the way out to get the changes.
return [lst[0]] + add_position_recur(lst[1:], number_from)
and you need to return lst in your base case:
def add_position_recur(lst, number_from=0):
length = len(lst)
# base case
if (length <= 1):
lst = [x+1 for x in lst]
return lst
else:
lst = [x+1 for x in lst]
return [lst[0]] + add_position_recur(lst[1:], number_from)
>>> add_position_recur([1,2,3])
[2, 4, 6]
However, this is quite a complicated approach to this recursion. It is idiomatic for the base case to be the empty list, otherwise take the head and recurse down the tail. So something to consider which uses the number_from:
def add_position_recur(lst, number_from=1):
if not lst:
return lst
return [lst[0]+number_from] + add_position_recur(lst[1:], number_from+1)
>>> add_position_recur([1,2,3])
[2, 4, 6]
This also has the advantage(?) of not changing the passed in lst
Why don't you instead do something like this:
def func(lon, after=[]):
if not l:
pass
else:
v = len(lon) + lon[-1]
after.append(v)
func(lon[:-1], after)
return after[::-1]
The output of the function for the example you provided matches what you want.
Currently, you are simply adding 1 to each value of your list.
lst = [x+1 for x in lst]
Rather, you should be increasing a variable which is being added to x with each iteration of x in lst.
lst = [x+(lst.index(x)+1) for x in lst]
This solution assumes that you want the number being added to x to depend on its position in the list relative to the start of the list, rather than being dependent on the position of x relative to the first element which was >1. Meaning, do you want to add 1 or 3 to the value 2 in the following list? The solution above adds three.
lst = [0.5, 0.1, 2, 3]

Idiom for flattening a shallow nested list: how does it work?

I found this bit of code in a module I am working on:
l = opaque_function()
thingys = [x for y in l for x in y]
I can't read this. By experiment, I was able to determe that it is flattening a 2-level nested list, but the syntex is still opaque to me. It has obviously omitted some optional brackets.
>>> l = [[1,2],[3,4]]
>>> [x for y in l for x in y]
[1, 2, 3, 4]
My eyes want to parse it as either: [x for y in [l for x in y] ] or [ [x for y in l] for x in y ], but both of those fail due to y not being defined.
How should I be reading this?
(I suspect I will feel very embarassed when this is explained.)
This used to really confuse me. You should read it like a nested loop:
new_list = []
for y in l:
for x in y:
new_list.append(x)
becomes
for y in l for x in y [do] new_list.append(x)
becomes
[x for y in l for x in y]
From the list displays documentation:
When a list comprehension is supplied, it consists of a single expression followed by at least one for clause and zero or more for or if clauses. In this case, the elements of the new list are those that would be produced by considering each of the for or if clauses a block, nesting from left to right, and evaluating the expression to produce a list element each time the innermost block is reached.
Thus, your expression can be rewritten as:
thingys = []
for y in l:
for x in y:
thingys.append(x)
You should read this as:
for y in l:
for x in y:
yield x
That's the generator version, but all comprehensions have the same basic syntax: while the x is put up front, the rest of the expression is still read left-to-right. I was confused by this at first too, expecting it to be the other way around, but it makes sense once you add filtering expressions:
>>> l = [[1,2,3,4,5], [1,"foo","bar"], [2,3]]
>>> [x for y in l
... if len(y) < 4
... for x in y
... if isinstance(x, int)]
[1, 2, 3]
Now imagine having to write this entire thing backwards:
[x if isinstance(x, int)
for x in y
if len(y) < 4
for y in l]
That would be confusing even to veteran Prolog programmers, not to mention the people maintaining Python parsers :)
The current syntax also matches that in Haskell, which inspired list comprehensions in the first place.
lis=[x for y in l for x in y] is Equivalent to:
lis=[]
for y in l:
for x in y:
lis.append(x)

Categories

Resources