Comparing values in two lists in Python - python

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."

Related

Nested loop with conditional list comprehension

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])

Return lists that do not have 1s

I want to create what I thought was a fairly straightforward function. The function just runs through a list of lists and returns any list that does not have a 1 in all of the list elements following the second element ([2: ]). So given the list of lists [[1, 2, 1, 1, 1, 1], [4, 5, 1, 2, 0.3, 1, 1, 1]] the function would return [4, 5, 1, 2, 0.3, 1, 1, 1]. What I have so far is:
def discover(A):
"""Looks for list that has an element not equal to one.
"""
for i in range(len(A)):
for j in range(len(A[i])):
if A[i][j+2] != 1:
print A[i]
But when I run the function it finds one list but then prints that list over and over again before giving me an IndexError saying the list index is out of range. This seems to be a fairly easy problem but for some reason I'm not getting it. Any help would be really appreciated.
The problem is these two lines:
for j in range(len(A[i])):
if A[i][j+2] != 1:
What'll happen is that you'll eventually get to a point where j is the length of your list, minus 1. But then you're calling j+2 in the below code, and that's guaranteed to create a number longer than your list, giving you the IndexError. You can fix that with:
for j in range(2,len(A[i])):
if A[i][j] != 1:
As for the endless printing, you're nearly there, but you'll want to stop the loop if you find the non-1 element.
if A[i][j] != 1:
print A[i]
break
Alternately, the other answers will give you the same result more easily. But that's where your current errors are coming from.
for list in A:
if 1 not in list[3:]:
print list
even another solution:
lst = [
[1,2,3],
[1,1,1],
[3,4,5],
[3,5,6],
] # +++
def has1(subLst):
return subLst.count(1) == 0
print filter(has1, lst)
This avoids out of range issues.
def discover(A):
results = []
for lst in A:
for i in lst[3:]:
if i != 1:
results.append(lst)
break
return results
In addition to the other answers here, one could also make use of a generator. The yield statement will allow you to skirt establishing a default list to place your results into; you can just specify the condition you're looking for and yield the result.
>>> def discover(lists):
... for l in lists:
... if not [x for x in l[2:] if x != 1]:
... yield l
>>> stuff = [[2, 3, 4, 5, 1, 2], [2, 5, 1, 1, 1, 1, 1]]
>>> results = discover(stuff) #returns <generator object discover at 0x105e3eb90>
>>> results.next()
[2, 5, 1, 1, 1, 1, 1]
>>>
The magic line here being, if not [x for x in l[2:] if x !=1]. It builds a list from l[2:] and checks that any variable in there does not equal 1; if the list has no length, it means there are no non-1 entries in l[2:] and so it yields l.
A query to check if any element (after the second) != 1 would be:
any(x != 1 for x in mylist[3:])
so
def discover(A):
for mylist in A:
if any(x != 1 for x in mylist[3:]):
print mylist

Python: Searching for an int in a list

Say I have this list
x = [1,2,3,1,5,1,8]
Is there a way to find every index that 1 is in the list?
Sure. A list comprehension plus enumerate should work:
[i for i, z in enumerate(x) if z == 1]
And the proof:
>>> x = [1, 2, 3, 1, 5, 1, 8]
>>> [i for i, z in enumerate(x) if z == 1]
[0, 3, 5]
The questioner asked for a solution using list.index, so here is one such solution:
def ones(x):
matches = []
pos = 0
while True:
try:
pos = x.index(1, pos)
except ValueError:
break
matches.append(pos)
pos += 1
return matches
It is somewhat more verbose than mgilson's solution, which I would consider to be more idiomatic Python.

Function Adding Two Lists

EDIT: Thank you for the responses, which are helpful, but I'm not sure they're getting at the core of my problem. In some cases it would be nice to simply select the lower of the lists and then add the values accordingly, but in THIS case I actually want to treat uneven lists as though the shorter list had zero values for the values it was missing. So I want [1, 2, 3] + [1, 2] to function as [1, 2, 3] + [1, 2, 0]. I don't think zip, or reversing my operator, will accomplish this.
I'm trying to create a function that adds the corresponding values of two lists and returns a new list with the sums of each of the indices of the two original lists:
def addVectors(v1, v2):
print(v1[0], v1[1], v2[0])
newVector = []
if len(v1) > len(v2):
for index in range(len(v1)):
print(index)
newVector[index] += v1[index] + v2[index]
else:
for index in range(len(v2)):
print(index)
newVector[index] += v2[index] + v1[index]
return newVector
addVectors([1, 2, 3], [1, 2])
Yet I'm getting an error stating that the list index is out of range? Not sure what I'm doing wrong in this seemingly simple program....
You probably meant to change the line:
if len(v1) > len(v2):
to:
if len(v1) < len(v2):
That way, you are iterating to the number of elements in v1 when v1 is shorter, which prevents you from going over the edge.
Note that this would also throw an error because newVector is a list of length 0 and you are accessing outside of its range. You'd have to change
newVector[index] += v1[index] + v2[index]
to
newVector.append(v1[index] + v2[index])
However, note that this can be done much more simply as:
def addVectors(v1, v2):
return map(sum, zip(v1, v2))
ETA: To pad the list with zeros, do:
import itertools
def addVectors(v1, v2):
return map(sum, itertools.izip_longest(v1, v2, fillvalue=0))
For example:
addVectors([1, 2, 3, 4, 5], [1, 2])
# [2, 4, 3, 4, 5]
Why not just use this?
def sum_lists(a, b):
return [x[0] + x[1] for x in zip(a, b)]
sum_lists([1, 2, 3], [4, 5, 6]) # -> [5, 7, 9]
You compare the lengths of the list, which is correct. But then you change the required operations. I.e. when list1 is longer than list2, you should only loop over the elements for the length of list2.
To fill the lists of uneven length, you can use itertools:
>>> import itertools
>>> map(sum, itertools.izip_longest([1,2,3], [1,2], fillvalue = 0))
[2, 4, 3]
Your problem lies here:
if len(v1) > len(v2):
for index in range(len(v1)):
print(index)
newVector[index] += v1[index] + v2[index]
You ensure that len(v1) > len(v2), but then iterate over range(len(v1)).
In your example, you're trying to access v2[2], which doesn't exist.
UPDATE:
In response to your edit, you could use something like this:
def addVectors(v1, v2):
if len(v1) > len(v2):
map(sum, zip(v1, v2)).extend(v1[len(v2):])
else:
map(sum, zip(v1, v2)).extend(v2[len(v1):])
Your IndexError is because you're trying to write to newVector[index] when newVector is an empty list. You need to either initialize it to a bunch of zeros, or use append instead.
>>> first = [1,2,3]
>>> second = [1,2]
>>> output = []
>>> for i, item in enumerate(first):
... additional = second[i] if i < len(second) else 0
... output.append(item + additional)
...
>>> output
[2, 4, 3]
And to ensure that len(first) > len(second), you can do something like this:
first, second = first, second if len(first) > len(second) else second, first
Or you can try
def add_vector(vector1, vector2):
index = len(vector1) - 1
new = []
while index >= 0:
result = vector1[index] + vector2[index]
new.append(result)
index -=1
new.reverse()
return new

How to check if all elements of a list match a condition?

I have a list consisting of like 20000 lists. I use each list's 3rd element as a flag. I want to do some operations on this list as long as at least one element's flag is 0, it's like:
my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]
In the beginning, all flags are 0. I use a while loop to check if at least one element's flag is 0:
def check(list_):
for item in list_:
if item[2] == 0:
return True
return False
If check(my_list) returns True, then I continue working on my list:
while check(my_list):
for item in my_list:
if condition:
item[2] = 1
else:
do_sth()
Actually, I wanted to remove an element in my_list as I iterated over it, but I'm not allowed to remove items as I iterate over it.
Original my_list didn't have flags:
my_list = [["a", "b"], ["c", "d"], ["e", "f"], .....]
Since I couldn't remove elements as I iterated over it, I invented these flags. But the my_list contains many items, and while loop reads all of them at each for loop, and it consumes lots of time! Do you have any suggestions?
The best answer here is to use all(), which is the builtin for this situation. We combine this with a generator expression to produce the result you want cleanly and efficiently. For example:
>>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
True
>>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
False
Note that all(flag == 0 for (_, _, flag) in items) is directly equivalent to all(item[2] == 0 for item in items), it's just a little nicer to read in this case.
And, for the filter example, a list comprehension (of course, you could use a generator expression where appropriate):
>>> [x for x in items if x[2] == 0]
[[1, 2, 0], [1, 2, 0]]
If you want to check at least one element is 0, the better option is to use any() which is more readable:
>>> any(flag == 0 for (_, _, flag) in items)
True
If you want to check if any item in the list violates a condition use all:
if all([x[2] == 0 for x in lista]):
# Will run if all elements in the list has x[2] = 0 (use not to invert if necessary)
To remove all elements not matching, use filter
# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)
You could use itertools's takewhile like this, it will stop once a condition is met that fails your statement. The opposite method would be dropwhile
for x in itertools.takewhile(lambda x: x[2] == 0, list)
print x
this way is a bit more flexible than using all():
my_list = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
all_zeros = False if False in [x[2] == 0 for x in my_list] else True
any_zeros = True if True in [x[2] == 0 for x in my_list] else False
or more succinctly:
all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]
Another way to use itertools.ifilter. This checks truthiness and process
(using lambda)
Sample-
for x in itertools.ifilter(lambda x: x[2] == 0, my_list):
print x

Categories

Resources