I received this exercise:
Write a function enumerate that takes a list and returns a list of
tuples containing (index,item) for each item in the list
My problem is that I cannot insert the index and value in one or a combination of for loops. This is the code I managed to make:
a = ["a", "b", "c","a","b","c"]
index = 0
for i in a:
print (index,i)
index+=1
This is roughly the code I want to produce (must be on one line):
my_enumerate = lambda x :[(t) for t in x )]
print list(my_enumerate(range(4)))
How can I put it all one lambda line to get (value, index) back? The output should look like:
[(0, "a"), (1, "b"), (2, "c")]
If you can actually index just add the value by indexing:
my_enumerate = lambda x :[(t, x[t]) for t in range(len(x))]
print list(my_enumerate(a))
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'a'), (4, 'b'), (5, 'c')]
If not use zip and put range in the lambda:
my_enumerate = lambda x: zip(range(len(x), x))
print list(my_enumerate(a))
[(i,a[i])for i in range(len(a))]
my_enumerate = lambda x: [(i, x[i]) for i in xrange(len(x))]
a = ["a", "b", "c", "a", "b", "c"]
print my_enumerate(a)
outputs:
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'a'), (4, 'b'), (5, 'c')]
EDIT: use range instead of xrange and print(...) instead of print if you are using python3
Python's zip and range functions solve this problem pretty handily.
my_enumerate = lambda seq: zip(range(len(seq)), seq)
In Python 2.x, you should use itertools.izip, and xrange instead.
You could also do it recursively:
>>> myenumerate = lambda l, n=0: [] if not l else (lambda ll = list(l): [(n, ll.pop(0))] + myenumerate(ll, n+1)()
list.pop(n) returns the nth value form the list, and returns it.
The only problem is that you must pass in a list:
>>> myenumerate([1,2,3,4,5,6,7,8])
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)]
>>> myenumerate("astring")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'str' object has no attribute 'pop'
>>> myenumerate(list("astring"))
[(0, 'a'), (1, 's'), (2, 't'), (3, 'r'), (4, 'i'), (5, 'n'), (6, 'g')]
However, if you just blindly added calls to list, you would't be able to replicate the functionality required without using a slice.
A neat trick for bypassing this requirement is to use another lambda:
>>> myenumerate = lambda l, n=0: [] if not l else (lambda ll: [(n, ll.pop(0))] + myenumerate(ll, n+1))(list(l))
>>> myenumerate("astring")
[(0, 'a'), (1, 's'), (2, 't'), (3, 'r'), (4, 'i'), (5, 'n'), (6, 'g')]
Related
I'm trying to implement a min heap on a list of tuple.
For example:
A=[('a',2),('b',1)]
how can I heapify A based on the second element of these tuple, so that A will be heapified to [('b',1),('a',2)] ? (I must maintain a min heap.)
As per #JimMischel's comment, place your tuples in a tuple with the priority as the first element. Then use heapq:
import heapq
list = [('a', 2), ('b', 1), ('c', 0), ('d', 1)]
heap_elts = [(item[1], item) for item in list]
heapq.heapify(heap_elts) # you specifically asked about heapify, here it is!
while len(heap_elts) > 0:
print(heapq.heappop(heap_elts)[1]) # element 1 is the original tuple
produces:
('c', 0)
('b', 1)
('d', 1)
('a', 2)
import heapq
A=[('a',2),('b',1), ('d', 0), ('c', 2), ('a', 2)]
h = []
for el in A:
heapq.heappush(h, (el[1], el[0]))
print(h)
result:
[(0, 'd'), (2, 'a'), (1, 'b'), (2, 'c'), (2, 'a')]
I have a method that build huffman tree which is as follows:
def buildTree(tuples) :
while len(tuples) > 1 :
leastTwo = tuple(tuples[0:2]) # get the 2 to combine
theRest = tuples[2:] # all the others
combFreq = leastTwo[0][0] + leastTwo[1][0] #enter code here the branch points freq
tuples = theRest + [(combFreq,leastTwo)] # add branch point to the end
tuples.sort() # sort it into place
return tuples[0] # Return the single tree inside the list
but while I feed the function with following parameter:
[(1, 'b'), (1, 'd'), (1, 'g'), (2, 'c'), (2, 'f'), (3, 'a'), (5, 'e')]
I get the error as
File "<stdin>", line 7, in buildTree
tuples.sort()
TypeError: '<' not supported between instances of 'tuple' and 'str'
While debugging I found the error was in tuples.sort().
The error is thrown because you are creating inner nodes in (priority, (node, node)) form. For equal priorities, Python then tries to compare a symbol from a leaf node (so the second element in a (priority, symbol) node tuple) with the (node, node) tuple from an inner node:
>>> inner = (combFreq, leastTwo)
>>> inner
(2, ((1, 'b'), (1, 'd')))
>>> theRest[1]
(2, 'c')
>>> theRest[1] < inner
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'tuple'
For building a huffman tree, if you want to sort your array of nodes, you only really need to sort on the priority, ignoring the rest of the tuples (symbols or child nodes):
tuples.sort(key=lambda t: t[0])
With that correction, your buildTree() function produces a tree:
>>> buildTree([(1, 'b'), (1, 'd'), (1, 'g'), (2, 'c'), (2, 'f'), (3, 'a'), (5, 'e')])
(15, ((6, ((3, 'a'), (3, ((1, 'g'), (2, 'c'))))), (9, ((4, ((2, 'f'), (2, ((1, 'b'), (1, 'd'))))), (5, 'e')))))
Personally, I'd use a priority queue instead, avoiding sorting each time. See How to implement Priority Queues in Python?
This question already has answers here:
Insert an item into sorted list in Python
(10 answers)
Closed 6 years ago.
I am trying to find a way to add a a tuple to a list while maintaining the order. For example I have a list.
[(1, 'a'), (3, 'y'), (4, 'd')]
and want to be able to automatically insert another tuple which may be (2, 'q') for example and it would automatically put it between (1, 'a') and (3, 'y') without me knowing the index.
Thanks in Advance
You could use the Python bisect module.
import bisect
l = [(1, 'a'), (3, 'y'), (4, 'd')]
bisect.insort(l, (2, 'q'))
print (l)
>> [(1, 'a'), (2, 'q'), (3, 'y'), (4, 'd')]
if your data is always sorted the bisect module will give you an efficient way to find the insertion point.
if the number determines the insertion point you can just do:
import bisect
data = [(1, 'a'), (3, 'y'), (4, 'd')]
bisect.insort(data, (2, 'q'))
print(data) # [(1, 'a'), (2, 'q'), (3, 'y'), (4, 'd')]
if the letter is the key to the insertion you need to keep a 'journal' of the letters in your list:
import bisect
data = [(1, 'a'), (3, 'y'), (4, 'd')]
keys = [r[1] for r in data] # precomputed list of keys
index = bisect.bisect(keys, 'q')
data.insert(index, (2, 'q'))
keys.insert(index, 'q')
print(data) # [(1, 'a'), (2, 'q'), (3, 'y'), (4, 'd')]
print(keys) # ['a', 'q', 'y', 'd']
list.insert(x[, start[, end]]) will do the actual inserting.
You can use list.insert. From the documentation:
Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).
Code
l = [(1, 'a'), (3, 'y'), (4, 'd')]
l.insert(1, (2, 'q'))
print(l)
Output
[(1, 'a'), (2, 'q'), (3, 'y'), (4, 'd')]
Edit: this answer assumes you just wanted to insert that particular tuple in the list, such that the new list remains ordered by the first tuple elements in ascending order. I'm not providing a solution for the general case if that's what you need.
use yourlist.insert(position,value)
li = [(1, 'a'), (3, 'y'), (4, 'd')]
li.insert(1,(2,'q'))
print "list :",li
output :
list : [(1, 'a'), (2, 'q'), (3, 'y'), (4, 'd')]
So I have these tuples in two lists in another tuple which look like
data_tuple = ([(1,2,3),(4,5,6)(7,8,9)],[(a,b,c),(d,e,f)(g,h,i)])
I want to build a set of coordinates using the corresponding indexed elements from each list, so that it looks like
final = [(2,b),(5,e),(8,h)]
Here's what I got:
for a in data_tuple[0]:
x = a[1]
for b in data_tuple[1]:
y = b[1]
print(x,y)
For now I just wanna check if my iteration/indentation is right so I don't need to put them in a list just yet. Right now the outcome for this particular code is
(2,b)
(2,e)
(2,h)
(5,b)
(5,e)
and so on until it reaches (8,h).
If I bring my print line to the left, under the second for loop, I get
(2,h)
(5,h)
(8,h)
How do I fix this? Sorry if I don't make any sense =/. As you can tell I'm extremely new to Python so I'm not familiar with a lot of import modules. Any help please?
>>> print [(i[0][1],i[1][1]) for i in zip(*data_tuple)]
[(2, 'b'), (5, 'e'), (8, 'h')]
but often clarity is more important than brevity
I dont really understand your original question since it looks like its working as intended that implies your indentation and iteration is fine..
[edit] this is what you want i think
for i in range(len(data_tuple[0]):
x,y = data_tuple[0][i][1],data_tuple[1][i][1],
print (x,y)
or
>>> for numbr,letr in zip(*data_tuple):
... x,y = numbr[1],letr[1]
... print(x,y)
...
Assuming you always want the second element:
[ (a, b) for ((_,a,_), (_,b,_)) in zip(*data_tuple) ]
Note that this produces the result you indicated, but isn't the same as your algorithm, which would produce the cartesian product. This would be achieved thus:
[ (a, b)
for (_,a,_) in data_tuple[0]
for (_,b,_) in data_tuple[1] ]
Assuming you always want the second element:
[ (a, b) for ((_,a,_), (_,b,_)) in zip(*data_tuple) ]
Note that this produces the result you indicated, but isn't the same as your algorithm, which would produce the cartesian product. This would be achieved thus:
[ (a, b)
for (_,a,_) in data_tuple[0]
for (_,b,_) in data_tuple[1] ]
EDIT: In response to your comment, here's the session I ran:
>>> [ (a, b)
... for (_,a,_) in data_tuple[0]
... for (_,b,_) in data_tuple[1] ]
[(2, 'b'), (2, 'e'), (2, 'h'), (5, 'b'), (5, 'e'), (5, 'h'), (8, 'b'), (8, 'e'), (8, 'h')]
This is the cartesian product, as per your original code.
Based on your amended question, however, I'm pretty sure you just want the first code I presented:
>>> [ (a, b) for ((_,a,_), (_,b,_)) in zip(*data_tuple) ]
[(2, 'b'), (5, 'e'), (8, 'h')]
Something like this?
>>> data_tuple = ([(1,2,3),(4,5,6),(7,8,9)],[("a","b","c"),("d","e","f"),("g","h","i")])
>>> full_numbers_list, full_letters_list = data_tuple
>>> desired_output_idx = (1,)
>>>
>>> results = []
>>> for numbers, letters in zip(full_numbers_list, full_letters_list):
... assert len(numbers) == len(letters) # make sure assumption is met
... for i in range(len(numbers)):
... if i in desired_output_idx:
... results.append((numbers[i], letters[i]))
...
>>> results
[(2, 'b'), (5, 'e'), (8, 'h')]
In place of using the steps of unpacking data_tuple you can use *data_tuple which will unpack your tuple for input to the zip() function.
>>> zip(*data_tuple)
[((1, 2, 3), ('a', 'b', 'c')), ((4, 5, 6), ('d', 'e', 'f')), ((7, 8, 9), ('g', 'h', 'i'))]
>>>
>>> # *data_tuple is the same as:
>>> numbers, letters = data_tuple
>>> zip(numbers, letters)
[((1, 2, 3), ('a', 'b', 'c')), ((4, 5, 6), ('d', 'e', 'f')), ((7, 8, 9), ('g', 'h', 'i'))]
I like to use the following idiom for combining lists together, sometimes:
>>> list(itertools.chain(*[[(e, n) for e in l] for n, l in (('a', [1,2]),('b',[3,4]))]))
[(1, 'a'), (2, 'a'), (3, 'b'), (4, 'b')]
(I know there are easier ways to get this particular result, but it comes comes in handy when you want to iterate over the elements in lists of lists of lists, or something like that. The trouble is, when you use generator expressions, this becomes error prone. E.g.
>>> list(itertools.chain(*(((e, n) for e in l) for n, l in (('a', [1,2]),('b',[3,4])))))
[(1, 'b'), (2, 'b'), (3, 'b'), (4, 'b')]
What's happening here is that the inner generator expressions get passed as arguments to itertools.chain, so at the the time they're evaluated, the outer generator expression has finished, and n is fixed at its final value, 'b'. I'm wondering whether anyone has thought of ways to avoid this kind of error, beyond "don't do that."
wouldn't a nested list comprehension be more appropriate?
>>> tt = (('a', [1,2]),('b',[3,4]))
>>> [(s, i) for i, l in tt for s in l]
[(1, 'a'), (2, 'a'), (3, 'b'), (4, 'b')]
Your approach almost works, you just need to flatten the generators. See how the for e in l is moved to the very right
>>> list(itertools.chain((e, n) for n, l in (('a', [1,2]),('b',[3,4])) for e in l ))
[(1, 'a'), (2, 'a'), (3, 'b'), (4, 'b')]
Here is how to do the same thing using itertools.product
>>> X=itertools.chain(*(itertools.product(*i[::-1]) for i in (('a', [1,2]),('b',[3,4]))))
>>> print list(X)
[(1, 'a'), (2, 'a'), (3, 'b'), (4, 'b')]
or if you are allowed to switch the tuples around
>>> X=itertools.chain(*(itertools.product(*i) for i in (([1,2],'a'),([3,4],'b'))))
>>> print list(X)
[(1, 'a'), (2, 'a'), (3, 'b'), (4, 'b')]
I'm going to suggest
data = (('a', [1,2]), ('b', [3,4]))
result = []
for letter, numbers in data:
for number in numbers:
result.append((number, letter))
It's a lot more readable than your solution family. Fancy is not a good thing.