I have 2 lists
l1 = [['a',1],['b',2],['c',3]]
l2 = [['b',2,10],['c',3,8]]
I want the below code to be replicated using list comprehension in python:
for i in range(len(l1)):
cnt = 0
for j in range(len(l2)):
if (l1[i][0]==l2[j][0]) & (l1[i][1]==l2[j][1]):
cnt = 1
if cnt==1:
isintb.append(1)
else:
isintb.append(0)
expected output: [0,1,1]
can you guys help??
I tried as below:
[[1 if (l1[i][0]==l2[j][0]) & (l1[i][1]==l2[j][1]) else 0 for j in range(len(l2))] for i in range(len(l1))]
got output as below:
[[0, 0], [1, 0], [0, 1]]
Since the inner for actually check if there is any match you can use any in the list comprehensions and cast the bool to int
isintb = [int(any((l1[i][0] == l2[j][0]) & (l1[i][1] == l2[j][1]) for j in range(len(l2)))) for i in range(len(l1))]
# [0, 1, 1]
There you go. You can try using any() of python to get the output.
any() basically checks if even one value is true in the iterable.
isintb = [int(any([1 if (l1[i][0]==l2[j][0]) and (l1[i][1]==l2[j][1]) else 0 for j in range(len(l2))])) for i in range(len(l1))]
You are required to convert the boolean into int since you want outputs of 0 or 1.
Docs:
any()
It would be more obvious to anyone who has to maintain your code if you just used a double loop. List comprehensions have their place. This isn't the place.
Something like this:
l1 = [['a', 1], ['b', 2], ['c', 3]]
l2 = [['b', 2, 10], ['c', 3, 8]]
isintb = []
for a, b in l1:
for x, y, _ in l2:
if a == x and b == y:
isintb.append(1)
break
else:
isintb.append(0)
print(isintb)
Output:
[0, 1, 1]
Simplifying a little by not using indices, and binary &:
print([int(any(i[0] == j[0] and i[1] == j[1] for j in l2))
for i in l1])
or even shorter:
print([int(any(i == j[:2] for j in l2)) for i in l1])
If you can change your input to lists of tuples:
l1 = [('a',1),('b',2),('c',3)]
l2 = [('b',2,10),('c',3,8)]
then this might be faster:
match = {j[:2] for j in l2}
print([int(i in match) for i in l1])
Related
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]]
I have a simple for loop like this:
for i in range(3):
if i == 1:
for x in [0, 2]:
free_spots.append([x, i])
else:
free_spots.append([1, i])
which outputs this list
[[1, 0], [0, 1], [2, 1], [1, 2]]
I planned to convert this to a list compehension like so but ran into a problem:
[[x, i] for x in [0, 2] if i == 1 else [1, i] for i in range(3)]
This causes an error, which I've tried fixing it like this:
[[[x, i] for x in [0, 2]] if i == 1 else [1, i] for i in range(3)]
and while that this now runs it also creates this:
[[1, 0], [[0, 1], [2, 1]], [1, 2]]
and I don't want items 2 and 3 to be in a list together. Is it possible to do something like this?:
[[0,i],[2,i] if i == 1 else [1, i] for i in range(3)]
If you really want to do it in one line, how about this?
free_spots = [val for sublist in [([[x, i] for x in [0,2]] if i == 1 else [[1, i]]) for i in range(3)] for val in sublist]
# This effectively creates a list of lists containing the values you want
main_list = [([[x, i] for x in [0,2]] if i == 1 else [[1, i]]) for i in range(3)]
# And then flattens it
free_spots = [val for sublist in main_list for val in sublist]
But the others are right. This is not a good idea unless you have a good reason. I can barely understand my own code now that I've written it which is not python zen.
You could do it in two steps.
free_spots = [[1, i] for i in range(3)]
free_spots[1:2] = [[x, 1] for x in [0, 2]]
The slice assignment is equivalent to the nested loop in the for loops.
However, this doesn't generalize well if you have multiple exceptions in the loop.
As already pointed out in the comments, it is not elegant to create such a list comprehension statement. Therefore, as suggested in Karl's comment, a generator is a preferred solution.
To use a generator, your code will look somehow like this:
# Create a generator function
def generate_data():
for i in range(3):
if i == 1:
for x in [0, 2]:
yield [x, i]
else:
yield [1, i]
# Use it
print(list(generate_data()))
I have a function that returns the indexes for all values that are true for the condition:
def check(samples_avg):
out = [[] for _ in samples_avg]
for indx, v in enumerate(zip(*samples_avg)):
m = max((val, i, indx) for i, val in enumerate(v))
if all(val * 5 < m[0] for i, val in enumerate(v) if i != m[1]):
out[m[1]].extend([m[2]])
print (out)
so for input: samples_avg = [[1, 12, 3], [15000, 4, 3], [1, 144, 45]]
the function returns out = [[], [0], [1, 2]]
However, I would like to add the ability for this function to return the element in a different list that is at the true index in the first list analyzed. So for example, for this list:
tracker = [['meow', 'woof', 'quack'], ['where', 'what', 'who'], ['dont', 'call', 'me']]
I'd expect an output like:
[[], ['where'], ['call', 'me']]
Because for samples_avg out was out = [[], [0], [1, 2]], or true at index 0 in the second list and indexes 2 and 3 in the last list (the first list is empty because nothing was true in it).
To do this I rewrote the function to look like this:
def check(samples_avg, tracker):
out = [[] for _ in samples_avg]
for indx, v in enumerate(zip(*samples_avg)):
m = max((val, i, indx) for i, val in enumerate(v))
if all(val * 5 < m[0] for i, val in enumerate(v) if i != m[1]):
out[m[1]].extend([m[2]])
g = [[] for _ in out]
for f in enumerate(zip(*out)):
g.append[enumerate(zip(out[f], tracker))]
print (out)
print (g)
However, this only returns
out = [[], [0], [1, 2]]
g = [[], [], []]
Any advice on how to fix this so it returns the values as expected would be greatly appreciated! Thank you!
You're making this more complicated than it needs to be.
Your main loop can fill in out as you already do. Then you can use a list comprehension to create the corresponding g list after the loop is done.
g = [[t[i] for i in o] for t, o in zip(tracker, out)]
I am new to python and trying to simply replace the values of a list when they meet a condition with values from a shorter list.
For example:
list1 = [1,0,1,0,1,0]
list2 = [1,2,3]
The output I want is:
list1 = [1,1,1,2,1,3]
I can use a loop with a counter:
counter = 0
for i, j in enumerate(list1):
if j == 1:
list1[i] = list2[counter]
counter += 1
But this seems inefficient for something so simple, so I'm guessing there might be a way to do this with a list comprehension, something like:
[list2[i] if j == 0 else j for i,j in enumerate(list1)]
(although this fails due the lists being different lengths).
Is there any other concise way of doing this in base python, perhaps using map or filter?
You can use an iterator made from the short list and just call next on it in the comprehension:
list1 = [1, 0, 1, 0, 1, 0]
list2 = [1, 2, 3]
it2 = iter(list2)
[x if x != 0 else next(it2) for x in list1]
# [1, 1, 1, 2, 1, 3]
Note that you can provide a default value to next if there are not enough filler elements:
Try something like:
[x if x else list2.pop(0) for x in list1]
Note this removes items from list2.
If you want to use map(), you could try this:
from collections import deque
list1 = [1,0,1,0,1,0]
list2 = [1,2,3]
queue = deque(list2)
result = list(map(lambda x : x if x else queue.popleft(), list1))
print(result)
Which outputs:
[1, 1, 1, 2, 1, 3]
Note: I used a stack/queue data structure, collections.deque to allow a O(1) popleft() from the front, instead of using pop(0), which is O(n). If you don't wish to use this library, you can just reverse list2 beforehand, and call pop(), which is also O(1).
You can try this:
list1 = [1,0,1,0,1,0]
list2 = [1,2,3]
new_list = [list2[list1[:i].count(a)] if a != 1 else a for i, a in enumerate(list1)]
Output:
[1, 1, 1, 2, 1, 3]
In Python 2.7, I have two lists of integers:
x = [1, 3, 2, 0, 2]
y = [1, 2, 2, 3, 1]
I want to create a third list which indicates whether each element in x and y is identical, to yield:
z = [1, 0, 1, 0, 0]
How can I do this using list comprehension?
My attempt is:
z = [i == j for i,j in ...]
But I don't know how to complete it.
You are looking for zip
z = [i == j for i,j in zip(x,y)]
But you better add int call to get your desired output
>>> z = [int(i == j) for i,j in zip(x,y)]
>>> z
[1, 0, 1, 0, 0]
else you'll get a list like [True, False, True, False, False]
As ajcr mentions in a comment, it is better to use itertools.izip instead of zip if the lists are very long. This is because it returns an iterator instead of a list. This is mentioned in the documentation
Like zip() except that it returns an iterator instead of a list.
demo
>>> from itertools import izip
>>> z = [int(i == j) for i,j in izip(x,y)]
>>> z
[1, 0, 1, 0, 0]
You can change it a little bit and do:
[x[i] == y[i] for i in xrange(len(x))]
If you use Python3 - change xrange to range
While a list comprehension was specified in the question and the answers above are probably better, I thought I'd chime in with a recursive solution:
def compare_lists(a, b, res=[]):
if len(a) == len(b):
if a == []:
return res
else:
if a[0] == b[0]:
res.append(1)
else:
res.append(0)
return compare_lists(a[1:], b[1:])
else:
return "Lists are of different length."