Many for in one line in python generator - python

I can't understand code with multi "for"s in one generator like this, I searched Google and didn't find any answer:
print [j for i in [[1,2],[4,6]] for j in i ]
# will print [1, 2, 4, 6]
print [j for j in i for i in [[1,2],[4,6]] ]
# will print [4, 4, 6, 6]
What is the difference?
How to interpret code like this?

Hopefully the expansions will help you reason with the code. It's a lot more readable this way, in my opinion. I'm printing one element at a time instead of the whole list.
print [j for i in [[1, 2], [4, 6]] for j in i]
is equivalent to
for i in [[1, 2], [4, 6]]:
for j in i:
print j
As a result i = [4, 6].
Now,
print [j for j in i for i in [[1,2],[4,6]]]
is equivalent to
for j in i: # Remember, i = [4, 6]
for i in [[1, 2], [4, 6]]:
print j

The second generater is error, but in your code scripts, the second generator, the i will be [4, 6] after your run the first generator, so the second will output [4, 4, 6, 6]

first list comprehension is read as
mat = [[1,2],[4,6]]
a = []
for i in mat:
for j in i:
a.append(j)
print(a)
second list comprehension is read as
b = []
for j in i:
for i in mat:
b.append(j)
print(b)

Comprehensions can be interpreted as a shorthand for loops whose only statement is to append an element to the list they create. The syntax you show is how nested loops are implemented.
For example, your first snippet [j for i in [[1,2],[4,6]] for j in i] can be rewritten as
result = []
for i in [[1,2],[4,6]]:
for j in i:
result.append(j)
Note that the order of the loops in the comprehension is the same as in the expanded form.
Your second snippet [j for j in i for i in [[1,2],[4,6]] then becomes
result = []
for j in i:
for i in [[1,2],[4,6]]:
result.append(j)
As you can see, this will simply fail with a NameError at the start of the first loop since i is not defined at that point.

Related

How do I convert a flat list to a list of lists based on value of the elements in python3?

I have a list
arr=[1,-1,4,-1,4,2]
Is there a way to convert it to [[1],[4],[4,2]]?
Basically the -1 indicates where a sub-list ends
I tried
number_of_sublists=arr.count(-1)
lst=[[]]*(number_of_sublists+1)
idx=0
for i in arr:
print(idx)
if i==-1:
idx+=1
else:
lst[idx].append(i)
print(lst)
but it is giving me the output
[[1,4,4,2],[1,4,4,2],[1,4,4,2]]
Why is this happening
Try this:
arr=[1,-1,4,-1,4,2]
newarr = [[]]
for i in arr:
if i != -1:
newarr[-1].append(i)
else:
newarr.append([])
print(newarr)
Output:
[[1], [4], [4, 2]]
You can use itertols.groupby:
from itertools import groupby
arr = [1, -1, 4, -1, 4, 2]
[[*g] for k, g in groupby(arr, key=(-1).__ne__) if k]
# [[1], [4], [4, 2]]
All the answers provided work for your task, but if you want to know why your original code did not work, it is because how you are declaring the list lst.
lst=[[]]*(number_of_sublists+1)
By doing this, you are repeating the same list at each of the positions of lst, that is, yo have made number_of_sublists+1 references to the same list. So when you modify one of them, you are actually modifying all at the same time.
You could simply change the declaration to this and that would solve your problem:
lst = [[] for _ in range(number_of_sublists+1)]
try the below
arr = [1, -1, 4, -1, 4, 2]
result = [[]]
create_new_list = False
for x in arr:
if x == -1:
create_new_list = True
else:
if create_new_list:
result.append([])
create_new_list = False
result[-1].append(x)
print(result)
output
[[1], [4], [4, 2]]

Separating sublist into multiple list and assigning dynamic list_name

I am trying to split a nested list into multiple lists and assign their name dynamically. Untill now, I tried the code below, but it only works when we have equal length sublists and we give them names manually.
sub_list = [[1,2,3],[4,5,5], [2,63,6]]
l1, l2, l3 = map(list, zip(*sub_list))
print(l1)
print(l2)
print(l3)
# Output
[1, 4, 2]
[2, 5, 63]
[3, 5, 6]
The approach above will fail when we have unequal length sublists such as (sub_list = [[1,2,3],[4,5], [2]]) and it does not give lists dynamic names.
I know it can be done by for loop, but I am not able to make list_name using a loop.
Any help will help me to reach more closure to my work
you could use zip_longest from itertools as follows:
sub_list = [[1,2,3],[4,5], [2]]
from itertools import zip_longest
l1, l2, l3 = map(list, zip_longest(*sub_list))
print(l1)
print(l2)
print(l3)
Output:
# [1, 4, 2]
# [2, 5, None]
# [3, None, None]
Answering the first question: If you don't want to give a manual name assing the map() to just one variable:
sub_list = [[1,2,3],[4,5,5], [2,63,6]]
rotated = map(list, zip(*sub_list))
for r in rotated:
print(r)
# Output
# [1, 4, 2]
# [2, 5, 63]
# [3, 5, 6]
Not completely sure what you want to accomplish, but I suggest you take a look at:
How to use itertools.zip_longest(): Python: zip-like function that pads to longest length? (You can filter out the Nones afterwards)
How to create dynamically named vars (although this is generally not the best thing to do): How do I create a variable number of variables?
The following code performs in both of your special cases:
There are no errors if some input lists are shorter than others
Names are procedurally/dynamically generated
def rotate_list_matrix(rows):
nrows = len(rows)
col_counts = map(lambda lyst: len(lyst), rows)
ncols = max(col_counts)
for ci in range(0, ncols): # column index
lyst = list()
list_name = "l" + str(ci + 1)
globals()[list_name] = lyst
for ri in range(0, nrows):
try:
lyst.append(rows[ri][ci])
except:
break
return
list_mata = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
list_matb = [[1, 2, 3],
[4, 5 ],
[7 ]]
rotate_list_matrix(list_matb)
print(l1)
print(l2)
print(l3)

Storing the result of a for loop in a array

I am trying to save the result of a nested for loop in a list in python. can someone tell me how to do it?
V is an array containing [1, 2, 3]
while n is the length = 3
and sq is the matrix containing swaps.
i have tried many approaches but whenever i return the result it only gives me one element of the list.
any help would be appreciated. Thankyou
def Permute1(sq,v,n):
for i in range(n):
for j in range(n):
if (sq[i,j]==1):
temp=v[i]
v[i]=v[j]
v[j]=temp
print(v)
results:
[1, 2, 3]
[2, 1, 3]
[3, 1, 2]
[3, 1, 2]
[3, 2, 1]
[3, 2, 1]
I'm not sure what is the utility of d = vhere.
To swap two elements in Python, I recommend:
v[i], v[j] = v[j], v[i]
Declaring an empty list before for loops and append the values (like AkshayNevrekar said) can also be useful, depending on what you need as a result.
def Permute1(sq,v,n):
result=[]
for i in range(n):
for j in range(n):
if (sq[i,j]==1):
temp=v[i]
v[i]=v[j]
v[j]=temp
result += [v]
print(result)
return result
No tested but may be it can help.
Check yield.
You can use this to generate all the permutations and process each one of them & store them in a list.

unite lists if at least one value matches in python

Let's say I have a list of lists, for example:
[[0, 2], [0, 1], [2, 3], [4, 5, 7, 8], [6, 4]]
and if at least one of the values on a list is the same that another one of a different list, i would like to unite the lists so in the example the final result would be:
[[0, 1, 2, 3], [4, 5, 6, 7, 8]]
I really don't care about the order of the values inside the list [0, 1, 2, 3] or [0, 2, 1, 3].
I tried to do it but it doesn't work. So have you got any ideas? Thanks.
Edit(sorry for not posting the code that i tried before):
What i tried to do was the following:
for p in llista:
for q in p:
for k in llista:
if p==k:
llista.remove(k)
else:
for h in k:
if p!=k:
if q==h:
k.remove(h)
for t in k:
if t not in p:
p.append(t)
llista_final = [x for x in llista if x != []]
Where llista is the list of lists.
I have to admit this is a tricky problem. I'm really curious what does this problem represent and/or where did you find it out...
I initially have thought this is just a graph connected components problem, but I wanted to take a shortcut from creating an explicit representation of the graph, running bfs, etc...
The idea of the solution is this: for every sublist, check if it has some common element with any other sublist, and replace that with their union.
Not very pythonic, but here it is:
def merge(l):
l = list(map(tuple, l))
for i, h in enumerate(l):
sh = set(h)
for j, k in enumerate(l):
if i == j: continue
sk = set(k)
if sh & sk: # h and k have some element in common
l[j] = tuple(sh | sk)
return list(map(list, set(l)))
Here is a function that does what you want. I tried to use self-documenting variable names and comments to help you understand how this code works. As far as I can tell, the code is pythonic. I used sets to speed up and simplify some of the operations. The downside of that is that the items in your input list-of-lists must be hashable, but your example uses integers which works perfectly well.
def cliquesfromlistoflists(inputlistoflists):
"""Given a list of lists, return a new list of lists that unites
the old lists that have at least one element in common.
"""
listofdisjointsets = []
for inputlist in inputlistoflists:
# Update the list of disjoint sets using the current sublist
inputset = set(inputlist)
unionofsetsoverlappinginputset = inputset.copy()
listofdisjointsetsnotoverlappinginputset = []
for aset in listofdisjointsets:
# Unite set if overlaps the new input set, else just store it
if aset.isdisjoint(inputset):
listofdisjointsetsnotoverlappinginputset.append(aset)
else:
unionofsetsoverlappinginputset.update(aset)
listofdisjointsets = (listofdisjointsetsnotoverlappinginputset
+ [unionofsetsoverlappinginputset])
# Return the information in a list-of-lists format
return [list(aset) for aset in listofdisjointsets]
print(cliquesfromlistoflists([[0, 2], [0, 1], [2, 3], [4, 5, 7, 8], [6, 4]]))
# printout is [[0, 1, 2, 3], [4, 5, 6, 7, 8]]
This solution modifies the generic breadth-first search to gradually diminish the initial deque and update a result list with either a combination should a match be found or a list addition if no grouping is discovered:
from collections import deque
d = deque([[0,2] , [0,1] , [2,3] , [4,5,7,8] , [6,4]])
result = [d.popleft()]
while d:
v = d.popleft()
result = [list(set(i+v)) if any(c in i for c in v) else i for i in result] if any(any(c in i for c in v) for i in result) else result + [v]
Output:
[[0, 1, 2, 3], [8, 4, 5, 6, 7]]

Python list append returning odd results

I'm trying to create a function that returns all the circular numbers from a given number, and it's giving me an odd result. The function is:
def getcircs(mylist):
circs=[]
x=mylist
y=list(x)
dig=len(x)-1
j=0
circs.append(x)
while j < dig:
for i in range(0,dig):
r=i+1
g=x[r]
y[i]=g
y[dig]=x[0]
print y
circs.append(y)
x=list(y)
j+=1
print circs
return circs
And as you can see when you run it, the lists 'y' are returning what I'm looking for, but the list 'circs' doesn't seem to have the right 'y' value appending to it. I'm wondering if it's an issue with the way Python references lists, but I can't figure it out. Thanks.
This is because lists are by reference and you re-use y. When you append y to circs, circs gets another reference to y. When you later modify y, you will see the change in each spot in circs where y was appended. Try making a copy of y and working with that.
def getcircs(mylist):
circs=[]
x=mylist
y=list(x)
dig=len(x)-1
j=0
circs.append(x)
while j < dig:
temp = y[:]
for i in range(0,dig):
r=i+1
g=x[r]
temp[i]=g
temp[dig]=x[0]
print temp
circs.append(temp)
x=list(temp)
j+=1
print circs
return circs
The line temp = y[:] just creates temp as a full slice of y, which natively produces a copy.
In case you want to do it a simpler way (in my opinion anyway) you might benefit from using itertools.cycle here. This approach could be cleaned up too, but you get the idea:
import itertools
my_list = [1, 2, 3, 4]
cycler = itertools.cycle(my_list)
list_size = len(my_list)
for i in range(list_size):
print [cycler.next() for j in range(list_size)] # call next on the cycler generator
cycler.next() # skip the next number
OUTPUT
[1, 2, 3, 4]
[2, 3, 4, 1]
[3, 4, 1, 2]
[4, 1, 2, 3]
In fact, here is a one-liner for it:
print [[cycler.next() for j in range(list_size + 1)][:-1] for i in range(list_size)]
OUTOUT
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]

Categories

Resources