Python: can I modify a Tuple? - python

I have a 2 D tuple (Actually I thought, it was a list.. but the error says its a tuple)
But anyways..
The tuple is of form:
(floatnumber_val, prod_id)
now I have a dictionary which contains key-> prod_id and value prod_name
now.. i want to change the prod_id in tuple to prod_name
So this is waht I did
#if prodName is the tuple
# prodDict is the dictionary
for i in range(len(prodName)):
key = prodName[i][1] # get the prodid
if prodDict.has_key(key):
value = prodDict[key]
prodName[i][1] = value
umm pretty straightforward
but i get an error that TypeError: 'tuple' object does not support item assignment
Thanks!!

Tuples are not mutable, you cannot change them.
The thing to do is probably to find out why you are creating tuples instead of the list you expected.

If prodName is a list of tuples and you want to create a new list of tuples like you explained, you have to create new tuples since a tuple is immutable (i.e. it can not be changed).
Example:
for i,(floatnumber_val, prod_id) in enumerate(prodName):
prodName[i] = (floatnumber_val, prodDict.get(prod_id,prod_id))

Short answer: you cannot.
Tuples are immutable. Lists are mutable. That's really the key distinction.
If you want to mutate an ordered collection of items in Python it's going to have to be a list. If you want to stick to tuples you're going to have to make a new one. If I've understood you correctly you are starting with:
prodName = [(1.0, 1), (1.1, 2), (1.2, 3)]
prodDict = {1: 'name_1', 2: 'name_2', 3: 'name_3'}
So, you can get the list you want with:
new_prodName = [(f, prodDict[id]) for f, id in prodName)]
This will fail if the id isn't found in the prodDict dict. If you want it to fail early that's great. If not, you can set a default (ex: None) using .get():
new_prodName = [(f, prodDict.get(id, None)) for f, id in prodName)]

Unfortunately, you can't modify the tuple. Use lists instead.

prodName[i] = tuple(prodName[i][0], value)

Related

convert python list into dictionary with key only , no value

I have a list of room names in python, however I need it to be a dictionary, simply in the form {"room1", "room2", "room3"}
Currently, my code can take the list and turn to a dictionary with both values and keys, i.e. {"room1":0, "room2":1, "room3": 2} etc.
my code is as follows:
rooms = ["G5a", "G5b", "G11"]
roomdict = dict(zip(rooms,range(len(rooms))))
print(roomdict)
But, this is not the format I need my dictionary to be in - thanks for your help in advanace :)
What you need is a set:
A set is an unordered collection of items. Every element is unique (no duplicates) and must be immutable (which cannot be changed).
rooms = ["G5a", "G5b", "G11"]
print(set(rooms))
OUTPUT:
{'G5b', 'G5a', 'G11'}
On the odd chance that your really need a dict with keys and empty values:
mydict = {"room1" : None, "room2": None, "room3" : None}
You could use the dict.fromkeys() method:
rooms = ["G5a", "G5b", "G11"]
roomdict = dict.fromkeys(rooms, None)
print(roomdict)
Output:
{'G5b': None, 'G11': None, 'G5a': None}

How to change specific value of a dictionary with multiple values (tuple) without getting TypeError

I have a function below which searches for a dictionary key match using an inputted function parameter. If a key match is found I want the value at index 1 (the team) to change to the desired team inputted when the function is called:
dict1 = {'Messi' : ('Argentina','Barcelona'), 'Ronaldo' : ('Portugal','Juventus'), 'Robben': ('Netherlands','Bayern')}
def setNewTeam(plyr, newTeam):
for x in dict1:
if plyr == x:
dict1[plyr][1] = newTeam
setNewTeam('Messi', 'Manchester')
When I run this code however, I get:
TypeError: 'tuple' object does not support item assignment
I know this must be because tuples are not mutable but there must be a way of making this work since i'm working with dictionaries, can anyone lend a hand here?
Thank you!
As the error message says, you cannot assign new items to tuples because tuples are immutable objects in python.
my_tup = (1,2,3)
my_tup[0] = 2 # TypeError
What you could do is using a list instead:
dict1 = {'Messi' : ['Argentina','Barcelona'], 'Ronaldo' : ['Portugal','Juventus'], 'Robben': ['Netherlands','Bayern']}
def setNewTeam(plyr, newTeam):
for x in dict1:
if plyr == x:
dict1[plyr][1] = newTeam
setNewTeam('Messi', 'Manchester')
Note how lists are created using [] while tuples use ().
dict1 = {'Messi' : ('Argentina','Barcelona'), 'Ronaldo' : ('Portugal','Juventus'), 'Robben': ('Netherlands','Bayern')}
def setNewTeam(plyr, newTeam):
for x in dict1:
if plyr == x:
dict1[plyr] = (dict1[plyr][0], newTeam)
setNewTeam('Messi', 'Manchester')
Since you want to update values, tuple is not the good data-structure. You should use a list.
If you still want to use a tuple, you can build a brand new tuple with :
dict1[plyr] = (dict1[plyr][0], newTeam)
dict1[plyr][1] = newTeam
Tuples are immutable, but lists are not. You can do something like:
list1 = list(dict1[plyr])
list1[1] = newTeam
dict1[plyr] = tuple(list1)
It will add the newTeam to your desired location, and it will still be a tuple.

Make a list element of multiple types in python

I want to make a list element that takes values from multiple types of variables.
I tried to do like this but it gives the error:
cannot concatenate 'str' and 'int' objects
name = "John"
age = 12
data = name + age
I want a list like this
data = [('John', 12)]
Just pack them into a tuple and create a list:
data = [(name, age)]
You can just to that :D
test = [None]*2
test[0] = "TEST"
test[1] = 25
print(test)
Gives:
['TEST',25]
I don't really see the problem since lists are capable of storing multiple types.
However, since you seem to want a list of names linked to age, you should look up dictionaries.
data = {'John':12 , 'Marie':14}
In this example data is a dictionary, it's like a list but instead of calling items like data[1] you can call data['Marie']and it will return you Marie's age (in this case).
If you want more information about dictionaries I suggest you look it up.
You can just give the type for each item you need, even if you have more list elements, like here:
list = [int(list[0]),float(list[1]), int(list[2]), float(list[3])]

Dicts in Python

I have a multidimensionnal dict, I need to return a specific value.
ConsomRatio={"DAP_Local":[],"MAP11_52":[]}
ConsomRatio["DAP_Local"].append({"Ammonia":"0.229", "Amine":"0.0007"})
ConsomRatio["MAP11_52"].append({"Ammonia":"0.138", "Fuel":"0.003"})
print(ConsomRatio["DAP_Local"])
The result of the print is:
[{'Ammonia': '0.229', 'Amine': '0.0007'}]
My question is : Is there a way to return the value of "Ammonia" only, in "DAP_Local" ?
Thank you!
You can get to it like this. You're appending your dict to a list, so you must select the correct index in the list where the dict is located. In this case the first element in the list or index 0.
ConsomRatio["DAP_Local"][0]["Ammonia"]
By the way, depending on what you are trying to achieve you might wanna take a look at the other answers for different implementations of multi-dimensional dicts.
The other answers are of course correct, but have you considered using a "dict of dicts"? i.e.:
ConsomRatio={"DAP_Local":{},"MAP11_52":{}}
ConsomRatio["DAP_Local"].update({"Ammonia":"0.229", "Amine":"0.0007"})
ConsomRatio["MAP11_52"].update({"Ammonia":"0.138", "Fuel":"0.003"})
print ConsomRatio["DAP_Local"]["Ammonia"]
0.229
since print(ConsomRatio["DAP_Local"]) returns an array of length 1, you need to select the index 0, then key off the 'Ammonia' value as above.
if print(ConsomRatio["DAP_Local"]) returned a dict, then no need to have the [0] and print(ConsomRatio["DAP_Local"]['Amomonia']) would have worked
Why are you putting lists in your dict, anyhow? You can just use dicts inside your main dict.
You can have multidimensional dicts also without the lists, e.g.:
ConsomRatio = {}
ConsomRation["DAP_Local"] = {"Ammonia":"0.229", "Amine":"0.0007"}
ConsomRatio["MAP11_52"] = {"Ammonia":"0.138", "Fuel":"0.003"}
print(ConsomRatio["DAP_Local"]["Ammonia"])
will give the desired result without the extra effort with the list.
You can get even shorter in Python:
ConsomRatio = {
"DAP_Local": {"Ammonia":"0.229", "Amine":"0.0007"},
"MAP11_52" : {"Ammonia":"0.138", "Fuel":"0.003"},
}
print(ConsomRatio["DAP_Local"]["Ammonia"])
To also answer your latest question (in your second comment):
to_produce = 'DAP_Local'
ingredience = 'Ammonia'
print('To produce {to_produce} we need {amount} of {ingredience}'.format(
to_produce=to_produce, ingredience=ingredience,
amount=ConsomRatio[to_produce].get(ingredience, '0.0')))
I hope, that helps!
It gets even better:
for product, ingred_list in ConsomRatio.items():
for iname, ivalue in ingred_list.items():
print('To produce {to_produce} we need {amount} of {ingredience}'
.format(to_produce=product, ingredience=iname,
amount=ivalue))

Shortest way to get first item of `OrderedDict` in Python 3

What's the shortest way to get first item of OrderedDict in Python 3?
My best:
list(ordered_dict.items())[0]
Quite long and ugly.
I can think of:
next(iter(ordered_dict.items())) # Fixed, thanks Ashwini
But it's not very self-describing.
Any better suggestions?
Programming Practices for Readabililty
In general, if you feel like code is not self-describing, the usual solution is to factor it out into a well-named function:
def first(s):
'''Return the first element from an ordered collection
or an arbitrary element from an unordered collection.
Raise StopIteration if the collection is empty.
'''
return next(iter(s))
With that helper function, the subsequent code becomes very readable:
>>> extension = {'xml', 'html', 'css', 'php', 'xhmtl'}
>>> one_extension = first(extension)
Patterns for Extracting a Single Value from Collection
The usual ways to get an element from a set, dict, OrderedDict, generator, or other non-indexable collection are:
for value in some_collection:
break
and:
value = next(iter(some_collection))
The latter is nice because the next() function lets you specify a default value if collection is empty or you can choose to let it raise an exception. The next() function is also explicit that it is asking for the next item.
Alternative Approach
If you actually need indexing and slicing and other sequence behaviors (such as indexing multiple elements), it is a simple matter to convert to a list with list(some_collection) or to use [itertools.islice()][2]:
s = list(some_collection)
print(s[0], s[1])
s = list(islice(n, some_collection))
print(s)
Use popitem(last=False), but keep in mind that it removes the entry from the dictionary, i.e. is destructive.
from collections import OrderedDict
o = OrderedDict()
o['first'] = 123
o['second'] = 234
o['third'] = 345
first_item = o.popitem(last=False)
>>> ('first', 123)
For more details, have a look at the manual on collections. It also works with Python 2.x.
Subclassing and adding a method to OrderedDict would be the answer to clarity issues:
>>> o = ExtOrderedDict(('a',1), ('b', 2))
>>> o.first_item()
('a', 1)
The implementation of ExtOrderedDict:
class ExtOrderedDict(OrderedDict):
def first_item(self):
return next(iter(self.items()))
Code that's readable, leaves the OrderedDict unchanged and doesn't needlessly generate a potentially large list just to get the first item:
for item in ordered_dict.items():
return item
If ordered_dict is empty, None would be returned implicitly.
An alternate version for use inside a stretch of code:
for first in ordered_dict.items():
break # Leave the name 'first' bound to the first item
else:
raise IndexError("Empty ordered dict")
The Python 2.x code corresponding to the first example above would need to use iteritems() instead:
for item in ordered_dict.iteritems():
return item
You might want to consider using SortedDict instead of OrderedDict.
It provides SortedDict.peekitem to peek an item.
Runtime complexity: O(log(n))
>>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
>>> sd.peekitem(0)
('a', 1)
If you need a one-liner:
ordered_dict[[*ordered_dict.keys()][0]]
It creates a list of dict keys, picks the first and use it as key to access the dictionary value.
First record:
[key for key, value in ordered_dict][0]
Last record:
[key for key, value in ordered_dict][-1]

Categories