Python:Update list of tuples - python

I have a list of tuples like this:
list = [(1, 'q'), (2, 'w'), (3, 'e'), (4, 'r')]
and i am trying to create a update function update(item,num) which search the item in the list and then change the num.
for example if i use update(w,6) the result would be
list = [(1, 'q'), (6, 'w'), (3, 'e'), (4, 'r')]
i tried this code but i had error
if item in heap:
heap.remove(item)
Pushheap(item,num)
else:
Pushheap(item,num)
Pushheap is a function that push tuples in the heap
any ideas?

You can simply scan through the list looking for a tuple with the desired letter and replace the whole tuple (you can't modify tuples), breaking out of the loop when you've found the required item. Eg,
lst = [(1, 'q'), (2, 'w'), (3, 'e'), (4, 'r')]
def update(item, num):
for i, t in enumerate(lst):
if t[1] == item:
lst[i] = num, item
break
update('w', 6)
print(lst)
output
[(1, 'q'), (6, 'w'), (3, 'e'), (4, 'r')]
However, you should seriously consider using a dictionary instead of a list of tuples. Searching a dictionary is much more efficient than doing a linear scan over a list.

As noted in the comments, you are using an immutable data structure for data items that you are attempting to change. Without further context, it looks like you want a dictionary, not a list of tuples, and it also looks like you want the second item in the tuple (the letter) to be the key, since you are planning on modifying the number.
Using these assumptions, I recommend converting the list of tuples to a dictionary and then using normal dictionary assignment. This also assumes that order is not important (if it is, you can use an OrderedDict) and that the same letter does not appear twice (if it does, only the last number will be in the dict).
>>> lst = [(1, 'q'), (2, 'w'), (3, 'e'), (4, 'r')]
>>> item_dict = dict(i[::-1] for i in lst)
>>> item_dict
{'q': 1, 'r': 4, 'e': 3, 'w': 2}
>>> item_dict['w'] = 6
>>> item_dict
{'q': 1, 'r': 4, 'e': 3, 'w': 6}

Tuples are an immutable object. Which means once they're created, you can't go changing there contents.
You can, work around this however, by replaceing the tuple you want to change. Possibly something such as this:
def change_item_in_list(lst, item, num):
for pos, tup in enumerate(lst):
if tup[1] == item:
lst[pos] = (num, item)
return
l = [(1, 'q'), (2, 'w'), (3, 'e'), (4, 'r')]
print(l)
change_item_in_list(l, 'w', 6)
print(l)
But as #brianpck has already said, you probably want a (ordered)-dictionary instead of a list of tuples.

Related

Compare List of Tuples and Return Indices of Matched Values

I'm new to programming and am having some trouble with this exercise. The goal is to write a function that returns a list of matching items.
Items are defined by a tuple with a letter and a number and we consider item 1 to match item 2 if:
Both their letters are vowels (aeiou), or both are consonants
AND
The sum of their numbers is a multiple of 3
NOTE: The return list should not include duplicate matches --> (1,2) contains the same information as (2,1), the output list should only contain one of them.
Here's an example:
***input:*** [('a', 4), ('b', 5), ('c', 1), ('d', 3), ('e', 2), ('f',6)]
***output:*** [(0,4), (1,2), (3,5)]
Any help would be much appreciated!
from itertools import combinations
lst = [('a', 4), ('b', 5), ('c', 1), ('d', 3), ('e', 2), ('f',6)]
vowels = 'aeiou'
matched = [(i[0],j[0]) for (i,j) in combinations(enumerate(lst),2) if (i[1][0] in vowels) == (j[1][0] in vowels) and ((i[1][1] + j[1][1]) % 3 == 0)]
print(matched)
Sorry, I'm high enough rep to comment, but i'll edit / update once I can.
Im a little confused about the question, what is the purpose of the letters, should we be using their positon in the alphabet as their value? i.e a=0, b=1?
what are we comparing one tuple to?
Thanks
You can use itertools.combinations with enumerate to iterate all combinations and output indices. Combinations do not include permutations, so you will not see duplicates.
from itertools import combinations
lst = [('a', 4), ('b', 5), ('c', 1), ('d', 3), ('e', 2), ('f',6)]
def checker(lst):
vowels = set('aeiou')
for (idx_i, i), (idx_j, j) in combinations(enumerate(lst), 2):
if ((i[0] in vowels) == (j[0] in vowels)) and ((i[1] + j[1]) % 3 == 0):
yield idx_i, idx_j
res = list(checker(lst))
# [(0, 4), (1, 2), (3, 5)]

Name of Algorithm(s) for Looping over a Variable Number of Independent Lists

Please excuse the vagueness of my question, I don't have any formal training in CS. I'm pretty sure a solution already exists, but I can't find an answer because I don't know what question to ask.
Essentially, I'm looking for the name of an algorithm, or group of algorithms, to find all combinations of several lists where each list contains the possibilities for a single position. That is, some function that can perform the mapping:
((a,b,c), (1,2), (z,g,h), (7)) ->
((a,1,z,7), (a,1,g,7), (a,1,h,7), (a,2,z,7), ... (c,2,h,7))
such that the result can be used to iterate over all possible combinations of the per-position lists in order.
The number of lists is variable, and the size of each list is variable and independent of the other lists. All the example solutions are missing at least one of those criteria.
It would also be awesome if anyone knew of pseudocode or example implementations for any of those algorithms, or a Python package that can handle this.
I can and have solved the problem previously, but I would like to know if there are better solutions out there before I implement my design again.
Thanks for your time.
For those curious, my previous solution looked something like what is described in this paper, which is the only example solution I've found. However, it doesn't name the problem, nor does it give me a jumping off point for further research. Here is an (untested) Python listing of my general solution:
def nloop(*args):
lists = args
n = len(lists) # Number of lists
# Exit if no lists were passed
if n <= 0:
raise StopIteration
i = [0] * n # Current index of each list
l = [len(L) for L in lists] # Length of each list
# Exit if any list is zero-length
if min(l) <= 0:
raise StopIteration
while True:
# Create and yield a list using the current indices
yield tuple( lists[x][ i[x] ] for x in range(n) )
# Increment the indices for the next loop
# Move to the next item in the last list
i[-1] += 1
# Check the lists in reverse order to carry any
# indices that have wrapped
for x in reverse(list(range(n))):
if i[x] >= l[x]:
i[x] = 0
if x > 0:
i[x-1] += 1
# If the first list has wrapped, we're done
if i[0] >= l[0]:
break
raise StopIteration
You are looking for itertools.product. It is equivalent to the nested for loop of arbitrary depth that you describe.
For your particular example (with appropriate syntactic modifications):
>>> from itertools import product
>>> list(product(('a', 'b', 'c'), (1, 2), ('z', 'g', 'h'), (7,)))
[('a', 1, 'z', 7),
('a', 1, 'g', 7),
('a', 1, 'h', 7),
('a', 2, 'z', 7),
('a', 2, 'g', 7),
('a', 2, 'h', 7),
('b', 1, 'z', 7),
('b', 1, 'g', 7),
('b', 1, 'h', 7),
('b', 2, 'z', 7),
('b', 2, 'g', 7),
('b', 2, 'h', 7),
('c', 1, 'z', 7),
('c', 1, 'g', 7),
('c', 1, 'h', 7),
('c', 2, 'z', 7),
('c', 2, 'g', 7),
('c', 2, 'h', 7)]

Smart way to delete tuples

I having a list of tuple as describes below (This tuple is sorted in decreasing order of the second value):
from string import ascii_letters
myTup = zip (ascii_letters, range(10)[::-1])
threshold = 5.5
>>> myTup
[('a', 9), ('b', 8), ('c', 7), ('d', 6), ('e', 5), ('f', 4), ('g', 3), ('h', 2), \
('i', 1), ('j', 0)]
Given a threshold, what is the best possible way to discard all tuples having the second value less than this threshold.
I am having more than 5 million tuples and thus don't want to perform comparison tuple by tuple basis and consequently delete or add to another list of tuples.
Since the tuples are sorted, you can simply search for the first tuple with a value lower than the threshold, and then delete the remaining values using slice notation:
index = next(i for i, (t1, t2) in enumerate(myTup) if t2 < threshold)
del myTup[index:]
As Vaughn Cato points out, a binary search would speed things up even more. bisect.bisect would be useful, except that it won't work with your current data structure unless you create a separate key sequence, as documented here. But that violates your prohibition on creating new lists.
Still, you could use the source code as the basis for your own binary search. Or, you could change your data structure:
>>> myTup
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f'),
(6, 'g'), (7, 'h'), (8, 'i'), (9, 'j')]
>>> index = bisect.bisect(myTup, (threshold, None))
>>> del myTup[:index]
>>> myTup
[(6, 'g'), (7, 'h'), (8, 'i'), (9, 'j')]
The disadvantage here is that the deletion may occur in linear time, since Python will have to shift the entire block of memory back... unless Python is smart about deleting slices that start from 0. (Anyone know?)
Finally, if you're really willing to change your data structure, you could do this:
[(-9, 'a'), (-8, 'b'), (-7, 'c'), (-6, 'd'), (-5, 'e'), (-4, 'f'),
(-3, 'g'), (-2, 'h'), (-1, 'i'), (0, 'j')]
>>> index = bisect.bisect(myTup, (-threshold, None))
>>> del myTup[index:]
>>> myTup
[(-9, 'a'), (-8, 'b'), (-7, 'c'), (-6, 'd')]
(Note that Python 3 will complain about the None comparison, so you could use something like (-threshold, chr(0)) instead.)
My suspicion is that the linear time search I suggested at the beginning is acceptable in most circumstances.
Here's an exotic approach that wraps the list in a list-like object before performing bisect.
import bisect
def revkey(items):
class Items:
def __getitem__(self, index):
assert 0 <= index < _len
return items[_max-index][1]
def __len__(self):
return _len
def bisect(self, value):
return _len - bisect.bisect_left(self, value)
_len = len(items)
_max = _len-1
return Items()
tuples = [('a', 9), ('b', 8), ('c', 7), ('d', 6), ('e', 5), ('f', 4), ('g', 3), ('h', 2), ('i', 1), ('j', 0)]
for x in range(-2, 12):
assert len(tuples) == 10
t = tuples[:]
stop = revkey(t).bisect(x)
del t[stop:]
assert t == [item for item in tuples if item[1] >= x]
Maybe a bit faster code than of #Curious:
newTup=[]
for tup in myTup:
if tup[1]>threshold:
newTup.append(tup)
else:
break
Because the tuples are ordered, you do not have to go through all of them.
Another possibility would also be, to use bisection, and find the index i of last element, which is above threshold. Then you would do:
newTup=myTup[:i]
I think the last method would be the fastest.
Given the number of tuples you're dealing with, you may want to consider using NumPy.
Define a structured array like
my_array= np.array(myTup, dtype=[('f0',"|S10"), ('f1',float)])
You can access the second elements of your tuples with myarray['f1'] which gives you a float array. Youcan know use fancy indexing techniques to filter the elements you want, like
my_array[myarray['f1'] < threshold]
keeping only the entries where your f1 is less than your threshold..
You can also use itertools e.g.
from itertools import ifilter
iterable_filtered = ifilter(lambda x : x[1] > threshold, myTup)
If you wanted an iterable filtered list or just:
filtered = filter(lambda x: x[1] > threshold, myTup)
to go straight to a list.
I'm not too familiar with the relative performance of these methods and would have to test them (e.g. in IPython using %timeit).

filtering a list of tuples in python

Am looking for a clean pythonic way of doing the following
I have a list of tuples say :
[(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
I want to make a new list which discards tuples whose first key has been seen before. So the o/p for the above would be:
[(1,'c'), (2,'d'), (5, 'f')]
Thanks!
A simple way would be creating a dictionary, since it will only keep the last element with the same key:
In [1]: l = [(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
In [2]: dict(l).items()
Out[2]: [(1, 'c'), (2, 'd'), (5, 'f')]
Update: As #Tadeck mentions in his comment, since the order of dictionary items is not guaranteed, you probably want to use an ordered dictionary:
from collections import OrderedDict
newl = OrderedDict(l).items()
If you actually want to keep the first tuple with the same key (and not the last, your question is ambiguous), then you could reverse the list first, add it do the dictionary and reverse the output of .items() again.
Though in that case there are probably better ways to accomplish this.
Using unique_everseen from itertools docs
from itertools import ifilterfalse
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
a = [(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
print list(unique_everseen(a,key=lambda x: x[0]))
Yielding
[(1, 'a'), (2, 'd'), (5, 'e')]
a nifty trick for one liner fetishists that keeps the order in place (I admit it's not very readable but you know...)
>>> s = [(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
>>> seen = set()
>>> [seen.add(x[0]) or x for x in s if x[0] not in seen]
[(1, 'a'), (2, 'd'), (5, 'e')]

Performance_Python get union of 2 lists of tuple according to 2 out of the 3 elements of the tuple

My program is not doing a great job. In a loop, data from each processor (list of tuple) are gathered into the master processor that needs to clean it by removing similar element.
I found a lot of interesting clue on internet and especially in this site about union of list. However, i have not managed to apply it to my problem.
My aim is to get rid of tuple whose its two last element are similar to another tuple in the list . for example:
list1=[[a,b,c],[d,e,f],[g,h,i]]
list2=[[b,b,c],[d,e,a],[k,h,i]]
the result should be:
final=[[a,b,c],[d,e,f],[g,h,i],[d,e,a]]
Right now I'm using loops and break but I'm hoping to make this process faster.
here is what my code looks like (result and temp are the lists I want to get union from)
on python2.6.
for k in xrange(len(temp)):
u=0
#index= next((j for j in xrange(lenres) if temp[k][1:3] == result[j][1:3]),None)
for j in xrange(len(result)):
if temp[k][1:3] == result[j][1:3]:
u=1
break
if u==0:
#if index is None:
result.append([temp[k][0],temp[k][1],temp[k][2]])
Thanks for your help
Herve
Below is our uniques function. It takes arguments l (list) and f (function), returns list with duplicates removed (in the same order). Duplicates are defined by: b is duplicate of a iff f(b) == f(a).
def uniques(l, f = lambda x: x):
return [x for i, x in enumerate(l) if f(x) not in [f(y) for y in l[:i]]]
We define lastTwo as follows:
lastTwo = lambda x: x[-2:]
For your problem we use it as follows:
>>> list1
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i')]
>>> list2
[('b', 'b', 'c'), ('d', 'e', 'a'), ('k', 'h', 'i')]
>>> uniques(list1+list2, lastTwo)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('d', 'e', 'a')]
If the usecase you describe comes up a lot you may want to define
def hervesMerge(l1, l2):
return uniques(l1+l2, lambda x: x[-2:])
Identity is our default f but it can be anything (so long as it is defined for all elements of the list, since they can be of any type).
f can be sum of a list, odd elements of a list, prime factors of an integer, anything. (Just remember that if its injective theres no point! Add by constant, linear functions, etc will work no differently than identity bc its f(x) == f(y) w/ x != y that makes the difference)
>>> list1
[(1, 2, 3, 4), (2, 5), (6, 2, 2), (3, 4), (8, 3), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)]
>>> uniques(list1, sum)
[(1, 2, 3, 4), (2, 5), (8, 3)]
>>> uniques(list1, lambda x: reduce(operator.mul, x)) #product
[(1, 2, 3, 4), (2, 5), (3, 4), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)]
>>> uniques([1,2,3,4,1,2]) #defaults to identity
[1, 2, 3, 4]
You seemed concerned about speed, but my answer really focused on shortness/flexibility without significant (or any?) speed improvment. For bigger lists where speed is z concern, you want to take advantage of hashable checks and the fact that list1 and list2 are known to have no duplicates
>>> s = frozenset(i[-2:] for i in list1)
>>> ans = list(list1) #copy list1
>>> for i in list2:
if i[-2:] not in s: ans.append(i)
>>> ans
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('d', 'e', 'a')]
OR allowing disordering
>>> d = dict()
>>> for i in list2 + list1:
d[i[-2:]] = i
>>> d.values()
[('d', 'e', 'f'), ('a', 'b', 'c'), ('g', 'h', 'i'), ('d', 'e', 'a')]
--Edit--
You should always be able to avoid un-pythonic looping like you post in your question. Here is your exact code with the loops changed:
for k in temp:
u=0
for j in result:
if k[1:3] == j[1:3]:
u=1
break
if u==0:
#if index is None:
result.append([k[0],k[1],k[2]]) // k
result and temp are iterable, and for anything iterable you can put it directly in the for loop without eanges. If for some reason you explicitly need the index (this is not such a case, but I have one above) you can use enumerate.
Here's a simple solution using a set:
list1=[('a','b','c'),('d','e','f'),('g','h','i')]
list2=[('b','b','c'),('d','e','a'),('k','h','i')]
set1 = set([A[1:3] for A in list1])
final = list1 + [A for A in list2 if A[1:3] not in set1]
However, if your list1 and list2 aren't actually made of tuples, then you will have to put tuple() around A[1:3].

Categories