Python: Filter in tuples - python

I have two lists of tuples. I want a new list with every member of l2 and every member of l1 that does not begin with the same element from l2.
I used a for loop and my output is ok.
My question is: How can I use the filter function or a list comprehension?
def ov(l1, l2):
l3=l1.copy()
for i in l2:
for j in l1:
if i[0]==j[0]:
l3.pop(l3.index(j))
print (l3+l2)
ov([('c','d'),('c','e'),('a','b'),('a', 'd')], [('a','c'),('b','d')])
The output is:
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]

If I understand correctly, this should be the straight forward solution:
>>> l1 = [('c','d'),('c','e'),('a','b'),('a', 'd')]
>>> l2 = [('a','c'),('b','d')]
>>>
>>> starters = set(x for x, _ in l2)
>>> [(x, y) for x, y in l1 if x not in starters] + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]
This can be generalized to work with longer tuples with extended iterable unpacking.
>>> starters = set(head for head, *_ in l2)
>>> [(head, *tail) for head, *tail in l1 if head not in starters] + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]

Here is an approach using filter:
from operator import itemgetter
f = itemgetter(0)
zval = set(map(itemgetter(0), l2))
list(filter(lambda tup: f(tup) not in zval, l1)) + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]
Or:
def parser(tup):
return f(tup) not in zval
list(filter(parser, l1)) + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]

Filter is a function which returns a list for all True returns of a function, being used as filter(function(), iterator).
def compare(one, two):
for i in two:
if i[0]==one[0]:
print("yes:", one,two)
return False
return True
l1 = [('c','d'),('c','e'),('a','b'),('a', 'd')]
l2 = [('a','c'),('b','d')]
one_liner = lambda n: compare(l1[n], l2) # where n is the tuple in the first list
lets_filter = list(filter(one_liner, range(len(l1))))
final_list = l2.copy()
for i in lets_filter:
final_list.append(l1[i])
print(final_list)
I made this as a way to do it. Lambda might be a bit confusing, alert if you don't understand it, and I'll remake it.
List comprehension is a "ternary operator", if you're familiar with those, in order to make a list in a one-liner.
l1 = [('c','d'),('c','e'),('a','b'),('a', 'd')]
l2 = [('a','c'),('b','d')]
l3 = [l1[n] for n in range(len(l1)) if l1[n][0] not in [l2[i][0] for i in range(len(l2))]]+l2
print(l3)
This code does the trick, but is overwhelming at first. Let me explain what it does.
l1[n] for n in range(len(l1) goes through all the pairs in l1, in order to see if we can add them. This is done when the if returns True.
l1[n][0] not in takes the first item, and returns True if doesn't exist in any of the elements of the following list.
[l2[i][0] for i in range(len(l2))] makes a list from all the first elements of l2.
+l2 is added, as requested.
As a bonus, I'm going to explain how to use else in the same scenario, in case you wanted another result.
l1 = [('c','d'),('a','b'),('c','e'),('a', 'd')]
l2 = [('a','c'),('b','d')]
l3 = [l1[n] if l1[n][0] not in [l2[i][0] for i in range(len(l2))] else ("not", "you") for n in range(len(l1))]+l2
print(l3)
As you can see, I had to switch the order of the operators, but works as it should, adding them in the correct order of l1 (which I changed for the sake of showing).

Related

nested value from nested index python

I am trying to get an output list from nested list based on nested indices.
Input:
list_a = [(a,b,c,d), (f,g), (n,p,x)]
sub_index_a = [(0,2),(1),(0,1)]
Output:
output_list = [(a,c), (g), (n,p)]
list_a = [('a', 'b', 'c', 'd'), ('f', 'g'), ('n', 'p', 'x')]
sub_index_a = [(0, 2), (1,), (0, 1)]
def check(i, e):
r = []
for ee in e:
r.append(list_a[i][ee])
return tuple(r)
outputlist = [check(i, e) for i, e in enumerate(sub_index_a)]
print(outputlist)
This evaluates to
[('a', 'c'), ('g',), ('n', 'p')]
well having ("g") just evaluates to "g",the actual tuple of that would look like ("g",) (tuple(["g"])), same as (1) but I think found a half-decent workaround? Hopefully, this is your desired solution.
list_a = [('a','b','c','d'), ('f','g'), ('n','p','x')]
sub_index_a = [(0,2),(1),(0,1)]
print([tuple([list_a[x][indx] for indx in i]) if type(i) in [tuple, list] else list_a[x][i] for x,i in enumerate(sub_index_a)])
[('a', 'c'), 'g', ('n', 'p')]
if you want everything returned as a tuple you can modify the comprehension to:
print([tuple([list_a[x][indx] for indx in i]) if type(i) in [tuple, list] else tuple([list_a[x][i]]) for x,i in enumerate(sub_index_a)])
[('a', 'c'), ('g',), ('n', 'p')]
note:
if you want everything to be nested (with a single element) you would want a list of lists; E.g. [[0,2],[1],[0,1]]
Use zip and a nested comprehension:
list_a = [("a","b","c","d"), ("f","g"), ("n","p","x")]
sub_index_a = [(0,2),(1,),(0,1)] # note the comma in the second tuple
output_list = [tuple(sub[i] for i in i_s) for sub, i_s in zip(list_a, sub_index_a)]
# [('a', 'c'), ('g',), ('n', 'p')]

Convert list of tuples such that [(a,b,c)] converts to [(a,b),(a,c)]

Thoughts on how I would do this? I want the first value in the tuple to pair with each successive value. This way each resulting tuple would be a pair starting with the first value.
I need to do this: [(a,b,c)] --> [(a,b),(a,c)]
You can try this.
(t,)=[('a','b','c')]
[(t[0],i) for i in t[1:]]
# [('a', 'b'), ('a', 'c')]
Using itertools.product
it=iter(('a','b','c'))
list(itertools.product(next(it),it))
# [('a', 'b'), ('a', 'c')]
Using itertools.repeat
it=iter(('a','b','c'))
list(zip(itertools.repeat(next(it)),it))
# [('a', 'b'), ('a', 'c')]
a = [('a','b','c')]
a = a[0]
a = [tuple([a[0], a[index]]) for index in range(1, len(a))]
Try this !
A solution that uses itertools's combinations module.
from itertools import combinations
arr = (['a','b','c'])
for i in list(combinations(arr, 2)):
if(i[0]==arr[0]):
print(i ,end = " ")
This would give a solution ('a', 'b') ('a', 'c')
You can just append pairs of tuples to a list:
original = [(1,2,3)]
def makePairs(lis):
ret = []
for t in lis:
ret.append((t[0],t[1]))
ret.append((t[0],t[2]))
return ret
print(makePairs(original))
Output:
[(1, 2), (1, 3)]
If your tuples are arbitrary length you can write a simple generator:
def make_pairs(iterable):
iterator = iter(iterable)
first = next(iterator)
for item in iterator:
yield first, item
example result:
my_tuple = ('a', 'b', 'c', 'd')
list(make_pairs(my_tuple))
Out[170]: [('a', 'b'), ('a', 'c'), ('a', 'd')]
This is a memory-efficient solution.

Remove tuples with same elements in two lists

a=['-a', ('-c', 'd'), ('-d', 'c')]
b=['-b', ('c', '-d'), ('d', '-c')]
Basicly, for each list, if there is some tuple in another list that has the same elements with it, then remove all of those tuples have this elements from both lists.
(I was using sets instead of tuples, but somewhere in my code got an error
says: unhashable type: 'set', so I changed it to tuples...)
res=[]
for i in a:
for j in b:
if type(i) == tuple and type(j) == tuple:
if i[0] in j and i[1] in j:
res.append(i)
res.append(j)
a,b=list(set(a)-set(res)),list(set(b)-set(res))
print(a,b)
This gives a=['a'],b=['-b'], is there better methods (maybe some simple build in function) to do the same thing ?
More examples
>>>a=['-a', ('-c', 'd'), ('-d', 'c'), ('-d', 'c')]
>>>b=['-b', ('c', '-d'), ('d', '-c'), ('-d', 'c')]
>a=['a'],b=['-b']
>>>a=['-a', ('a', 'b'),('-c', 'd'), ('-d', 'c'), ('-d', 'c')]
>>>b=['-b', ('c', '-d'), ('d', '-c'), ('-d', 'c')]
>a=['a',('a', 'b')],b=['-b']
Use frozenset On the sub items and you will be able to use set.difference:
a = ['-a', ('a', 'b'),('-c', 'd'), ('-d', 'c'), ('-d', 'c')]
b = ['-b', ('c', '-d'), ('d', '-c'), ('-d', 'c')]
seta = {frozenset(i) if isinstance(i, tuple) else i for i in a}
setb = {frozenset(i) if isinstance(i, tuple) else i for i in b}
print(seta - setb, setb - seta)
Prints:
{'-a', frozenset({'a', 'b'})} {'-b'}
I only say this because you said you were using sets before but had issues. You can always turn the frozen sets back to tuple.
Please find the following answer. Basically, you just need to find out duplicates first.
a = ['-a', ('-c', 'd'), ('-d', 'c')]
b = ['-b', ('-c', 'd'), ('d', '-c')]
to_remove = set(a).intersection(set(b))
a = [i for i in a if i not in to_remove or type(i) != tuple]
b = [j for j in b if j not in to_remove or type(j) != tuple]
print a, b

How to separate elements of tuples into occurrences of pairs in Python?

I have a tuple that looks like:
t=(('a','b'),('a','c','d','e'),('c','d','e'))
I need to rearrange it so I have a new tuple that will look like:
t2=(('a','b'),('a','c'),('c','d'),('d','e'),('c','d'),('d','e'))
Basically the new tuple takes pairs (of 2) from each element of the old tuple. But I am not sure how to get started. Thanks for your help.
Use a generator expression with zip to pair and convert to a tuple at the end:
>>> t = (('a','b'),('a','c','d','e'),('c','d','e'))
>>> tuple((x) for tupl in t for x in zip(tupl, tupl[1:]))
(('a', 'b'), ('a', 'c'), ('c', 'd'), ('d', 'e'), ('c', 'd'), ('d', 'e'))
Try this out :
tuple([(t[i][j],t[i][j+1]) for i in range(len(t)) for j in range(len(t[i])-1)])
#[('a', 'b'), ('a', 'c'), ('c', 'd'), ('d', 'e'), ('c', 'd'), ('d', 'e')]
You can also try another way. If the problem is reduced to do this for one tuple alone :
def pairs(my_tuple):
return [(my_tuple[i],my_tuple[i+1]) for i in range(len(my_tuple)-1)]
Then this can be mapped for all the tuples
tuple(sum(list(map(pairs,t)),[]))
#(('a', 'b'), ('a', 'c'), ('c', 'd'), ('d', 'e'), ('c', 'd'), ('d', 'e'))
Explanation :
map(pairs,t) : maps the function pairs for every element in tuple t
list(map(pairs,t)) : output of the above
But as a nested list
[[[('a', 'b')], [('a', 'c'), ('c', 'd'), ('d', 'e')],...]
sum(list(...),[]) : Flattens out this nested list for the desired output
Here's what I came up with really quick
def transform(t):
out = []
for tup in t:
for i in range(0, len(tup) - 1):
out.append((tup[i], tup[i+1]))
return tuple(out)
You can use this easy to understand code:
t = (('a','b'),('a','c','d','e'),('c','d','e'))
t2 = []
for i in t:
for j in range(len(i)-1):
t2.append((i[j], i[j+1]))
t2 = tuple(t2)
Obviously it isn't very optimized like other answers but for an easy understanding it will be perfect.
That is something equivalent to:
t2 = tuple((i[j], i[j+1]) for i in t for j in range(len(i)-1))
That is a generator expression, something quite similar to list comprehension (it use brackets instead of square brackets) and they basically do similar things, or at least in basic codes like this one. I still don't understand very well their differences but the generators are one-time fasters while the list comprehension are slower but reusable...
Nevermind: the generator means:
t2 = tuple(...) # Make with the result array a tuple, otherwise it will be a list.
for i in t # Iterate over each item of t, they will by called i.
for i in t for j in range(len(i)) # Iterate over each item of t --called--> i and then iterate over the range(len(i)) --called--> j.
(i[j], i[j+1]) for i in t for j in range(len(i)) # The same as before but each time we get a new j (each time the second loop iterate) do --> (i[j], i[j+1])
I know, make two generator/list expression/comprehension on the same line is strange. I always look at an answer like this one to remember how to do that.
My old answer was:
t = (('a','b'),('a','c','d','e'),('c','d','e'))
t2 = []
for i in t:
for j in range(len(i)):
if j < len(i) - 1:
t2.append((i[j], i[j+1]))
t2 = tuple(t2)
But I notice that adding a -1 to the len() of the loop I can avoid that line, because I won't never get an out of index error.

Removing duplicates from tuples within a list

I have a list of tuples:
lst = [('a','b'), ('c', 'b'), ('a', 'd'), ('e','f'), ('a', 'b')]
I want the following output list:
output = [('a','b'), ('e','f')]
i.e I want to compare the elements of first tuple with remaining tuples and remove the tuple which contains either one or more duplicate elements.
My attempt:
I was thinking of using for loops, but that wont be feasible once i have very large list. I browsed through following posts but could not get the right solution:
Removing duplicates members from a list of tuples
How do you remove duplicates from a list in whilst preserving order?
If somebody could guide me the right direction, it will be very helpful. Thanks!
Assuming that you want "duplicates" of all elements to be suppressed, and not just the first one, you could use:
lst = [('a','b'), ('c', 'b'), ('a', 'd'), ('e','f'), ('a', 'b')]
def merge(x):
s = set()
for i in x:
if not s.intersection(i):
yield i
s.update(i)
gives
>>> list(merge(lst))
[('a', 'b'), ('e', 'f')]
>>> list(merge([('a', 'b'), ('c', 'd'), ('c', 'e')]))
[('a', 'b'), ('c', 'd')]
>>> list(merge([('a', 'b'), ('a', 'c'), ('c', 'd')]))
[('a', 'b'), ('c', 'd')]
Sets should help:
>>> s = map(set, lst)
>>> first = s[0]
>>> [first] + [i for i in s if not i & first]
[set(['a', 'b']), set(['e', 'f'])]
Or with ifilterfalse:
>>> from itertools import ifilterfalse
>>> s = map(set, lst)
>>> [first] + list(ifilterfalse(first.intersection, s))
[set(['a', 'b']), set(['e', 'f'])]

Categories

Resources