Python - input from list of tuples - python

I've declared a list of tuples that I would like to manipulate. I have a function that returns an option from the user. I would like to see if the user has entered any one of the keys 'A', 'W', 'K'. With a dictionary, I would say this: while option not in author.items() option = get_option(). How can I accomplish this with a list of tuples?
authors = [('A', "Aho"), ('W', "Weinberger"), ('K', "Kernighan")]

authors = [('A', "Aho"), ('W', "Weinberger"), ('K', "Kernighan")]
option = get_option()
while option not in (x[0] for x in authors):
option = get_option()
How this works :
(x[0] for x in authors) is an generator expression, this yield the [0]th element of each item one by one from authors list, and that element is then matched against the option. As soon as match is found it short-circuits and exits.
Generator expressions yield one item at a time, so are memory efficient.

How about something like
option in zip(*authors)[0]
We are using zip to essentially separate the letters from the words. Nevertheless, since we are dealing with a list of tuples, we must unpack it using *:
>>> zip(*authors)
[('A', 'W', 'K'), ('Aho', 'Weinberger', 'Kernighan')]
>>> zip(*authors)[0]
('A', 'W', 'K')
Then we simply use option in to test if option is contained in zip(*authors)[0].

There are good answers here that cover doing this operation with zip, but you don't have to do it like that - you can use an OrderedDict instead.
from collections import OrderedDict
authors = OrderedDict([('A', "Aho"), ('W', "Weinberger"), ('K', "Kernighan")])
Since it remembers its entry order, you can iterate over it without fear of getting odd or unusual orderings of your keys.

Related

How to remove the first element from a list of tuples?

I have a list of tuples like so:
x = [("a","b","c"), ("d","e","f"), ("g","h","i"), ("j","k","l")]
and I would like to remove the first element from each index like so:
x = [("b","c"), ("e","f"), ("h","i"), ("k","l")]
I've tried using pop.x and remove.x like so which doesn't work. I think because the list has tuples which cannot be changed?:
for i in result:
i.pop(0)
So I tried to convert the list of tuples to list of lists using a zip function but i get an error:
AttributeError: type object 'zip' has no attribute 'result'
Any ideas what i'm doing wrong?
Thanks for all the useful answers! Many ways to skin a cat that i can use for other problems i encounter =D
A tuple in python is an unchangeable object. You can store anything you want in it but once it is declared you cannot change it back.
Here a link to tuples : https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences (section 5.3)
The popfunction deletes the last element from your list and returns it. Since you have tuples in your list, calling the pop function on your list of tuples will only result in returning and deleting the last tuple from your list.
Tuples are immutable, so you can't change them. You can however overwrite your list with a list of new tuples:
x = [('a','b','c'), ('d','e','f'), ('g','h','i'), ('j','k','l')]
x = [tpl[1:] for tpl in x]
Output:
[('b', 'c'), ('e', 'f'), ('h', 'i'), ('k', 'l')]
As you've found, tuples are immutable so you must create a new list with tuples that don't contain the items
new_list = []
for i in result:
new_list.append(i[1:])
or replace the list items by their index
for idx, tup in enumerate(x):
x[idx] = tup[1:]
or as a list comprehension
[i[1:] for i in result]
x = [('a','b','c'), ('d','e','f'), ('g','h','i'), ('j','k','l')]
y = []
for t in x:
y.append(tuple(list(t)[1:]))
print(y)
Output
[('b', 'c'), ('e', 'f'), ('h', 'i'), ('k', 'l')]

Python: about sort

I noticed that the results are different of the two lines. One is a sorted list, while the other is a sorted dictionary. Cant figure out why adding .item will give this difference:
aa={'a':1,'d':2,'c':3,'b':4}
bb=sorted(aa,key=lambda x:x[0])
print(bb)
#['a', 'b', 'c', 'd']
aa={'a':1,'d':2,'c':3,'b':4}
bb=sorted(aa.items(),key=lambda x:x[0])
print(bb)
# [('a', 1), ('b', 4), ('c', 3), ('d', 2)]
The first version implicitly sorts the keys in the dictionary, and is equivalent to sorting aa.keys(). The second version sorts the items, that is: a list of tuples of the form (key, value).
When you iterate on dictionary then you get iterate of keys not (key, value) pair. The sorted method takes any object on which we can iterate and hence you're seeing a difference.
You can verify this by prining while iterating on the dict:
aa={'a':1,'d':2,'c':3,'b':4}
for key in aa:
print(key)
for key in aa.keys():
print(key)
All of the above two for loops print same values.
In the second example, items() method applied to a dictionary returns an iterable collection of tuples (dictionary_key, dictrionary_value). Then the collection is being sorted.
In the first example, a dictionary is automatically casted to an iterable collection of its keys first. (And note: only very first characters of each of them are used for comparinson while sorting, which is probably NOT what you want)

Pythonic way to iterate over a shifted list of tuples

Given a list L = [('a',3),('b',4),('c',14),('d',10)],
the desired output is the first item from a tuple and the second item from the next tuple, e.g.:
a 4
b 14
c 10
Straightforward but unpythonic way would be
for i in range(len(L)-1):
print(L[i][0], L[i+1][1])
Alternatively, this is what I've came up with:
for (a0,a1),(b0,b1) in zip(L,L[1:]):
print(a0,b1)
but it seems to be wasteful. Is there a standard way to do this?
I personally think both options are just fine It is possible to extract the items and join them:
pairs = zip(map(itemgetter(0), L), map(itemgetter(1), L[1:]))
# [('a', 4), ('b', 14), ('c', 10)]
A pythonic way is to use a generator expression.
You could write it like this:
for newTuple in ((L[i][0], L[i+1][1]) for i in range(len(L)-1)):
print(newTuple)
It looks like a list-comprehension, but the iterator-generator will not create the full list, just yields a tuple by tuple, so it is not taking additional memory for a full-copy of list.
To improve your zip example (which is already good), you could use itertools.islice to avoid creating a sliced copy of the initial list. In python 3, the below code only generates values, no temporary list is created in the process.
import itertools
L = [('a',3),('b',4),('c',14),('d',10)]
for (a0,_),(_,b1) in zip(L,itertools.islice(L,1,None)):
print(a0,b1)
I'd split the first and second items with the help of two generator expressions, use islice to drop one item and zip the two streams together again.
first_items = (a for a, _ in L)
second_items = (b for _, b in L)
result = zip(first_items, islice(second_items, 1, None))
print(list(result))
# [('a', 4), ('b', 14), ('c', 10)]

Replacing a single element in a tuple nested within a list - Is their a better way?

Edit - I want to change the value of a tuple nested in a list, at a specific position
eg changed nestedTuple[1][1] change to 'xXXXXx'
I have come up with this code, that works, but it just seems very 'Un-pure!'
Convert to a list - change - convert to tuple - insert back into list
I ASSuME that it would be very demanding on resources.
Could anyone please advise me if their is a better way?
>>> nestedTuple= [('a','b','c'), ('d','e','f'), ('g','h','i')]
>>> tempList = list(nestedTuple[1])
>>> tempList[1] = 'xXXXXx'
>>> nestedTuple[1] = tuple(tempList)
>>> print nestedTuple
[('a', 'b', 'c'), ('d', 'xXXXXx', 'f'), ('g', 'h', 'i')]
You can use slicing.
>>> i = 1
>>> nestedTuple = [('a','b','c'), ('d','e','f'), ('g','h','i')]
>>> nestedTuple[1] = nestedTuple[1][:i] + ('xXXXXx', ) + nestedTuple[1][i+1:]
>>> nestedTuple
[('a', 'b', 'c'), ('d', 'xXXXXx', 'f'), ('g', 'h', 'i')]
How about this?
nested_tuple[1] = tuple('XXXXX' if i==1 else x for i, x in enumerate(nested_tuple[1]))
Note that tuples aren't meant to be changed, so one liners aren't going to be very clean.
Depending on how many changes you want to make in your nestedTuple and depending on downstream in your program. You may want to built a nestedList from your nestedTuple
nestedList = [list(myTuple) for myTuple in nestedTuple]
and then do:
nestedList[x][y] = 'truc'
and then make a new nestedTuple if needed
Otherwise you should profile this
I understand that this is data structure that you are getting. Performance aside it would make for much cleaner and readable code to change the data to nested list, do the manipulation, and if you need to write it back to convert it back to nested tuple. May be suboptimal in terms of speed, but that might not be the limiting factor for your application.
nestedTuple= [('a','b','c'), ('d','e','f'), ('g','h','i')]
nestedList = [list(x) for x in nestedTuple]
now you can use normal list slicing and assigning
nestedList[1][1] = ['xxxxXXxxx']
if you need the data back in original nested tuple format use the one liner:
nestedTuple = [tuple(x) for x in nestedList]
most readable and least likely to contain bugs if your data structure grows and your slicing becomes more complex.
Very purpose to use tuples is its immutable means once the tuple is created the values cannot be changed. In your case the best way will be to use nested lists i.e., as shown below
>>> nestedList = [['a','b','c'], ['d','e','f'], ['g','h','i']]
Now to change the element 'e' in the list to 'xxxx' you can use as shown below
>>> nestedList[1][1] = 'xxxx'

Generate combinations of elements from multiple lists

I'm making a function that takes a variable number of lists as input (i.e., an arbitrary argument list).
I need to compare each element from each list to each element of all other lists, but I couldn't find any way to approach this.
Depending on your goal, you can make use of some of the itertools utilities. For example, you can use itertools.product on *args:
from itertools import product
for comb in product(*args):
if len(set(comb)) < len(comb):
# there are equal values....
But currently it's not very clear from your question what you want to achieve. If I didn't understand you correctly, you can try to state the question in a more specific way.
I think #LevLeitsky's answer is the best way to do a loop over the items from your variable number of lists. However, if purpose the loop is just to find common elements between pairs of items from the lists, I'd do it a bit differently.
Here's an approach that finds the common elements between each pair of lists:
import itertools
def func(*args):
sets = [set(l) for l in args]
for a, b in itertools.combinations(sets, 2):
common = a & b # set intersection
# do stuff with the set of common elements...
I'm not sure what you need to do with the common elements, so I'll leave it there.
The itertools module provides a lot of useful tools just for such tasks. You can adapt the following example to your task by integrating it into your specific comparison logic.
Note that the following assumes a commutative function. That is, about half of the tuples are omitted for reasons of symmetry.
Example:
import itertools
def generate_pairs(*args):
# assuming function is commutative
for i, l in enumerate(args, 1):
for x, y in itertools.product(l, itertools.chain(*args[i:])):
yield (x, y)
# you can use lists instead of strings as well
for x, y in generate_pairs("ab", "cd", "ef"):
print (x, y)
# e.g., apply your comparison logic
print any(x == y for x, y in generate_pairs("ab", "cd", "ef"))
print all(x != y for x, y in generate_pairs("ab", "cd", "ef"))
Output:
$ python test.py
('a', 'c')
('a', 'd')
('a', 'e')
('a', 'f')
('b', 'c')
('b', 'd')
('b', 'e')
('b', 'f')
('c', 'e')
('c', 'f')
('d', 'e')
('d', 'f')
False
True
if you want the arguments as dictionary
def kw(**kwargs):
for key, value in kwargs.items():
print key, value
if you want all the arguments as list:
def arg(*args):
for item in args:
print item
you can use both
def using_both(*args, **kwargs) :
kw(kwargs)
arg(args)
call it like that:
using_both([1,2,3,4,5],a=32,b=55)

Categories

Resources