python - iterating over a subset of a list of tuples - python

Lets say I have a list of tuples as follows
l = [(4,1), (5,1), (3,2), (7,1), (6,0)]
I would like to iterate over the items where the 2nd element in the tuple is 1?
I can do it using an if condition in the loop, but I was hoping there will a be amore pythonic way of doing it?
Thanks

You can use a list comprehension:
[ x for x in l if x[1] == 1 ]
You can iterate over tuples using generator syntax as well:
for tup in ( x for x in l if x[1] == 1 ):
...

How about
ones = [(x, y) for x, y in l if y == 1]
or
ones = filter(lambda x: x[1] == 1, l)

Just use the if. It's clear and simple.
for x, y in tuples:
if y == 1:
do_whatever_with(x)

Build a generator over it:
has_1 = (tup for tup in l if l[1] == 1)
for item in has_1:
pass

for e in filter(l, lambda x: x[1] == 1):
print e

Try this, using list comprehensions is the pythonic way to solve the problem:
lst = [(4,1), (5,1), (3,2), (7,1), (6,0)]
[(x, y) for x, y in lst if y == 1]
=> [(4, 1), (5, 1), (7, 1)]
Notice how we use tuple unpacking x, y to obtain each of the elements in the pair, and how the condition if y == 1 filters out only those element with value 1 in the second element of the pair. After that, you can do anything you want with the elements found, in particular, I'm reconstructing the original pair in this part at the left: (x, y)

Since you want to iterate, itertools.ifilter is a nice solution:
from itertools import ifilter
iter = ifilter(lambda x: x[1] == 1, l)
for item in iter:
pass

Related

lambda if with list of numbers in python

I try to print from a list, only the organs that match the condition (divided by 2) with the help of a Lambda and something here does not work:
list = list(range(0, 50))
[(lambda x: print(x) if(x % 2 == 0)(x(l)) for l in list]
No need for a lambda for this task, you can just use a list comprehension to generate the sub-list of elements matching the condition:
l = list(range(0, 50))
print([x for x in l if x % 2 == 0])
check it
x = [i for i in range(50) if not i %2]
You don't need to use lambda for this. You can do this way:
list = list(range(50))
[i for i in list if i%2==0]

Selection elements of a list based on another 'True'/'False' list

I have two lists of the same length.
The first one contains strings. The second one - strings that can be either 'True' or 'False'.
If the nth element of the second list is 'True', I want to append the nth element of the first list to another list.
So if I have:
List1:
('sth1','sth2','sth3','sth4')
List2:
('True','False','True','False')
The outcome should be List3:
('sth1','sth3').
How can I intersect two list in that way?
Use zip:
result = [x for x, y in zip(xs, ys) if y == 'True']
Example:
xs = ('sth1','sth2','sth3','sth4')
ys = ('True','False','True','False')
result = [x for x, y in zip(xs, ys) if y == 'True']
result
['sth1', 'sth3']
Or use numpy:
import numpy as np
filtered = np.array(List1)[np.array(List2)]
Btw this only works if the elements inside List2 are True/False, not if they are "True"/"False".
If you didn't know about zip :
l1 = ('sth1','sth2','sth3','sth4')
l2 = ('True','False','True','False')
l = [x for i,x in enumerate(l1) if l2[i]=='True']
print l
#=> ['sth1', 'sth3']
It would be shorter with a tuple of booleans :
l1 = ('sth1','sth2','sth3','sth4')
l2 = (True,False,True,False)
l = [x for i,x in enumerate(l1) if l2[i]]
print l
Simplest way is to use itertools.compress method as follows.
import itertools
list1 = ('sth1','sth2','sth3','sth4')
list2 = ('True','False','True','False')
list2 = map(lambda x: x == 'True', list2)
result = list(itertools.compress(list1, list2))
compress method returns an iterator, so you that is why you need to wrap the iterator object in list
I hope it helps.

Having an issues with list comprehension

def divisble_numbers(a_list, terms):
b_list = [x for x in [a_list] if (x % [terms] == 0)]
c_list = [x for x in b_list if all(x % [terms] == 0)]
return c_list
divisble_numbers([2,3,5,1,6,7,8,9,10,11,12], [2,3])
Returns this error: TypeError: unsupported operand type(s) for %: 'int' and 'list'
I am trying to get to get a list of the index that is divisible by both terms. I am confused on the error I am getting, very new to list comprehension would appreciate any help.
You were pretty close. This code should work:
def divisble_numbers(a_list, terms):
return [x for x in a_list if all(x % term == 0 for term in terms)]
print(divisble_numbers([2,3,5,1,6,7,8,9,10,11,12], [2,3]))
# Output:
# [6, 12]
There are two list comprehensions happening here. One is x for x in a_list if .... The other one is inside the all: x % term == 0 for term in terms.
Your list comprehensions are good, but you've accidentally wrapped a few things in square brackets, such as [terms], which don't need to be because they are already lists. [terms] will produce a list containing a list.
Second, the error that you were getting is because you were taking the mod (%) of a list. The mod operator only works between numbers.
def divisble_numbers(a_list, terms):
b_list = [x for x in a_list if (x % terms[0] == 0)]
c_list = [x for x in b_list if (x % terms[1] == 0)]
return c_list
b_list = [x for x in a_list if x%(reduce(lambda x,y : x*y, terms))==0]
Input :
a_list, terms = [2,3,5,1,6,7,8,9,10,11,12], [2,3]
Output :
[6, 12]
Your function will be :
def divisble_numbers(a_list, terms): return [x for x in a_list if x%(reduce(lambda x,y : x*y, terms))==0]

Removing elements corresponding to 'None' in lists

I have two lists
x = [ None , None , "foo" , "bar" ]
y = [ "bar" , "foo" , "foo" ,"bar"]
len(x) == len(y)
I want to check if there exists an element in list x that is None then remove that element and remove corresponding element in y too .
like remove x[0] == None , so remove x[0] and y[0] from x and y
the result should be:
x = ["foo","bar"]
y = ["foo","bar"]
i tried a quite non-pythonic way which gave me a "list index out of range " error:
for i in range(0,len(x)):
if(x[i] == None):
x.remove(x[i])
y.remove(y[i])
Using the zip method you can do this very nicely:
x, y = zip(*[(e_x, e_y) for e_x, e_y in zip(x, y) if e_x is not None])
Here you iterate over both lists at once, creating a new list with tuples containing the elements of x and y. These tuples are only added if the element from x e_x is not None.
The outer zip converts the list of tuples back in two separate lists.
Edit: As Donkey Kong pointed out in a comment, it is better to use is not None instead of != None. I updated the code accordingly.
Let's call your lists xs and ys.
ys = [y for (x, y) in zip(xs, ys) if x]
xs = [x for x in xs if x]
should do the trick.
First go through and figure out what you need to remove:
remove_ix = {ix for ix, val in enumerate(x) if val is None}
Now you can use it to filter x and y:
x = [item for ix, item in enumerate(x) if ix not in remove_ix]
y = [item for ix, item in enumerate(y) if ix not in remove_ix]
Note that with your version, you'll end up skipping indices and probably having IndexErrors because if the initial length of the list is N and you remove a single item, then you'll still be looping N times, but the list is now N-1 items long. Also with list.remove, you can't guarantee that you're removing the correct item if there are duplicates (which there are in your example)
Use zip to remove items that are None (in x) from matching pairs of x and y, then use another zip(*...) to transpose the list of lists back to the references x and y:
x, y = zip(*[[ix, iy] for ix, iy in zip(x, y) if ix is not None])
I think the most performant way to achieve desired output would be dict comprehension. In that case you avoid avoid 2 list comprehension expressions:
x_list = [ None , None , "foo" , "bar" ]
y_list = [ "bar" , "foo" , "foo" ,"bar"]
d = {val:y_list[i] for i,val in enumerate(x_list) if val is not None}
new_l_y = d.values()
new_l_x = d.keys()
NOTE: You would miss order of elements but keep correspondence of items. If order matters you can use OrderedDict.
from collections import OrderedDict
d = OrderedDict((val,y_list[i]) for i,val in enumerate(x_list) if val is not None)
new_l_y = d.keys()
new_l_y = d.values()
Posting an answer in a dead thread. It might help someone.
One very neat method is to use itertool's compress method.
from itertools import compress
x = [None, None, 1, 2]
y = [5, 6, 2, 4]
y = list(compress(y, x))
x = list(compress(x, x))

Python built-in list: remove one item

lst = [(32,(4,5)),(55,(5,6))]
Given a tuple (4,5), so I want to remove the first tuple in lst. How to do that?
Sorry, this wasn't clear, what I mean is remove the list item by the second item in each list item
lst2 = [(x, y) for (x, y) in lst if y != (4, 5)]
Removes ALL (4, 5) tuples, not just the first instance.
You could filter the list:
lst = filter(lambda x, y: y != (4,5), lst)
This removes the first list entry whose 2nd component matches, but don't use this to remove more than one. To do more than one use a list comp as others have suggested or iterate over a copy of the list (for eaxmple: for n in L[:]). Modifying a list that you are iterating has unexpected results.
>>> def remove(L,t):
... for n in L:
... if n[1]==t:
... L.remove(n)
... break
...
>>> L=[(1,(2,3)),(4,(5,6)),(7,(2,3))]
>>> remove(L,(2,3))
>>> L
[(4, (5, 6)), (7, (2, 3))]
Do you mean lst2 = [(a,) for (a,b) in lst]' ?
That will result in[(32,),(55,)]`

Categories