Add new item to Dictionary dynamically using a variable - python

I'm currently reading values from file and spliting them as parameter and value e.g. #id=7 becomes param = #id, value = 7. I would like to use the param variable as a new key in the dictionary. However, it is not working as expected. I'm using the following code.
list1 = {}
with open('C:/Temp/file1.txt') as f:
lines = f.read().splitlines()
for line in lines:
middle = line.find("=")
param = line[:middle]
value = line[middle+1:]
list1[param] = value
In this code, the dictionary key and value becomes 7.
Thanks in advance.

You have to define your dictionary (d is a nice name). You can do it this way:
with open('C:/Temp/file1.txt') as f:#
d = dict(line.strip().split('=', 1) for line in f)
for k,v in d.iteritems():
print("param = {0}, value = {1}".format(k,v))

If you are defining list1 as a dict list1 = {} then your print statement is incorrect.
print("param = " + list1[param] + ", value = " + value)
both list1[param] and value would be 7. since list1[param] would give you the value of it's contents and not it's key.
Try looking at the dictionary afterwards by printing it.
print(list1)

I know this wasn't what you were asking for, but I would suggest to look at ConfigParser. This is a standard way to use configuration files.

Related

how can I create nested dictionary keys and assign them values from a list of namespaced key value pairs?

I have env vars that looks like this:
CONFIG-SOMEKEY-SOMEOTHERKEY = val345
CONFIG-SOMEKEY-SOMEOTHEROTHERKEY = val678
CONFIG-ANOTHERKEY = val222
I want to create a dictionary out of them that would look like:
{
'SOMEKEY': {
'SOMEOTHERKEY': 'val3242',
'SOMEOTHEROTHERKEY': 'val678'
}
'ANOTHERKEY': 'val222'
}
"CONFIG-" is a prefix to denote which vars this should be done with- so I can filter them easily like this:
config_fields = [i for i in os.environ if i.startswith("CONFIG-")]
But I'm unsure of how to loop over the string, split on "-" and build a dict.
While looping I was thinking I could check if its the last item and assign the value but how would it know the full path of keys it's on?
I suspect this is a job for recursion I'm just now sure exactly how to implement it
You could do:
data = ['CONFIG-SOMEKEY-SOMEOTHERKEY = val345',
'CONFIG-SOMEKEY-SOMEOTHEROTHERKEY = val678',
'CONFIG-ANOTHERKEY = val222']
result = {}
for e in data:
key, value = e.split(" = ") # split into key and value
path = key.split("-") # split the key into parts
ref = result
for part in path[1:-1]:
ref[part] = part in ref and ref[part] or {}
ref = ref[part]
ref[path[-1]] = value # take the last part of key and set the value
print(result)
Output
{'SOMEKEY': {'SOMEOTHERKEY': 'val345', 'SOMEOTHEROTHERKEY': 'val678'}, 'ANOTHERKEY': 'val222'}
This part:
ref = result
for part in path[1:-1]:
ref[part] = part in ref and ref[part] or {}
ref = ref[part]
ref[path[-1]] = value
will create the nested dictionaries, is equivalent to:
for part in path[1:-1]:
if part not in ref:
ref[part] = {}
ref = ref[part]
So if the part is in the dictionary you set ref as the value corresponding to part otherwise you create a new dictionary.
You can use the assoc_in function from toolz. Split the name on - and slice off the prefix.
import os
from toolz.dictoolz import assoc_in
CONFIG={}
for k, v in os.environ.items():
if k.startswith("CONFIG-"):
assoc_in(CONFIG, k.split('-')[1:], v)
If you don't want to add a dependency, you can see the implementation of assoc_in here. A simpler substitute might be something like
def assoc_in(d, ks, v):
for k in ks[:-1]:
d = d.setdefault(k, {})
d[ks[-1]] = v
This uses the .setdefault() method to get the nested dicts, which will add a new one if it doesn't exist yet.
You can get your environment variables like so:
import os
text = [f"{k} = {v}" for k,v in os.environ.items() if k.startswith("CONFIG-")]
print(env)
(inspired by How to access environment variable values? - especially this answer)
Then you can use dicts to iterativly splitting your values:
text = """CONFIG-SOMEKEY-SOMEOTHERKEY = val345
CONFIG-SOMEKEY-SOMEOTHEROTHERKEY = val678
CONFIG-ANOTHERKEY = val222"""
text = text.split("\n")
d = {}
curr_d = d
for part in text:
while "-" in part:
a, b = part.split("-",1)
if '-' in b:
curr_d [a] = curr_d.get(a,{})
curr_d = curr_d[a]
part = b
a, b = part.split("=",1)
curr_d[a] = b
curr_d = d
print(d)
Output:
{'CONFIG': {'SOMEOTHERKEY ': ' val345',
'SOMEOTHEROTHERKEY ': ' val678'},
'ANOTHERKEY ': ' val222'}

In Python, How to assign value of variable to the dictionary, where the variable will keep getting values for each iteration

Ex:
for x in myresult:
y=str(x)
if y.startswith('(') and y.endswith(')'):
y = y[2:-3]
y=y.replace("\\","").replace(";",'')
chr_num = y.find("property_name")
chr_num=chr_num+15
PropertyName = y[chr_num:-1]
chr_num1 = y.find("phrase_value")
chr_num1 = chr_num1 + 14
chr_num2 = y.find("where")
chr_num2=chr_num2-2
PhraseValue = y[chr_num1:chr_num2]
This is the existing code. Now i want to store 'PhraseValue' in dictionary or array.
NOTE: PhraseValue will keep getting values for each iteraction
This is a very basic question. In your case, obviously, PropertyName and PhraseValue are overwritten on each iteration and contains only the last values at the end of the loop.
If you want to store multiple values, the easiest structure is a list.
ret = [] # empty list
for x in some_iterator():
y = some_computation(x)
ret.append(y) # add the value to the list
# ret contains all the y's
If you want to use a dict, you have to compute a key and a value:
ret = {} # empty dict
for x in some_iterator():
y = some_computation(x)
k = the_key(x) # maybe k = x
ret[k] = y # map k to y
# ret contains all the k's and their mapped values.
The choice between a list and a dict depends on your specific problem: use a dict if you want to find values by key, like in a dictionary; use a list if you need ordered values.
Assuming that PropertyName is the key, then you could simply add
results = {}
before the loop, and
results[PropertyName] = PhraseValue
as the last line of the if statement inside the loop.
This solution does have one problem. What if a given PropertyName occurs more than once? The above solution would only keep the last found value.
If you want to keep all values, you can use collections.defaultdict;
import collections
results = collections.defaultdict(list)
Then as the last line of the if statement inside the loop;
results[PropertyName].append(PhraseValue)

How to check that only one value of my dictionary is filled?

How can I check that my dict contains only one value filled ?
I want to enter in my condition only if the value is the only one in my dict and of this type (in my example "test2") of my dict.
For now I have this if statement
my_dict = {}
my_dict["test1"] = ""
my_dict["test2"] = "example"
my_dict["test3"] = ""
my_dict["test4"] = ""
if my_dict["test2"] and not my_dict["test1"] and not my_dict["test3"] and not my_dict["test4"]:
print("inside")
I would like to find a better, classy and "pep8" way to achieve that
Any ideas ?
You have to check every value for truthiness, there's no way around that, e.g.
if sum(1 for v in my_dict.values() if v) == 1:
print('inside')
You can use filter() as below to check how many values are there in the dictionary.
if len(list(filter(None, my_dict.values()))) == 1:
print("inside")
Assuming that all your values are strings, what about
ref_key = "test2"
if ''.join(my_dict.values()) == my_dict[ref_key]:
print("inside")
... since it looks like you have a precise key in mind (when you do if my_dict["test2"]). Otherwise, my answer is (twice) less general than (some) others'.
Maybe you want to check if there's only one pair in dictionary after removing the empty values.
my_dict = {}
my_dict["test1"] = ""
my_dict["test2"] = "example"
my_dict["test3"] = ""
my_dict["test4"] = ""
my_dict={key:val for key,val in my_dict.items() if val}
if len(my_dict)==1:
print("inside")
Here is the another flavour (without loops):
data = list(my_dict.values())
if data.count('') + 1 == len(data):
print("Inside")

function would not change the parameter as wanted

here is my code
def common_words(count_dict, limit):
'''
>>> k = {'you':2, 'made':1, 'me':1}
>>> common_words(k,2)
>>> k
{'you':2}
'''
new_list = list(revert_dictionary(count_dict).items())[::-1]
count_dict = {}
for number,word in new_list:
if len(count_dict) + len(word) <= limit:
for x in word:
count_dict[x] = number
print (count_dict)
def revert_dictionary(dictionary):
'''
>>> revert_dictionary({'sb':1, 'QAQ':2, 'CCC':2})
{1: ['sb'], 2: ['CCC', 'QAQ']}
'''
reverted = {}
for key,value in dictionary.items():
reverted[value] = reverted.get(value,[]) + [key]
return reverted
count_dict = {'you':2, 'made':1, 'me':1}
common_words(count_dict,2)
print (count_dict)
what i expected is to have the count_dict variable to change to {'you':2}.
It did work fine in the function's print statement, but not outside the function..
The problem, as others have already written, is that your function assigns a new empty dictionary to count_dict:
count_dict = {}
When you do this you modify the local variable count_dict, but the variable with the same name in the main part of your program continues to point to the original dictionary.
You should understand that you are allowed to modify the dictionary you passed in the function argument; just don't replace it with a new dictionary. To get your code to work without modifying anything else, you can instead delete all elements of the existing dictionary:
count_dict.clear()
This modifies the dictionary that was passed to the function, deleting all its elements-- which is what you intended. That said, if you are performing a new calculation it's usually a better idea to create a new dictionary in your function, and return it with return.
As already mentioned, the problem is that with count_dict = {} you are not changing the passed in dictionary, but you create a new one, and all subsequent changes are done on that new dictionary. The classical approach would be to just return the new dict, but it seems like you can't do that.
Alternatively, instead of adding the values to a new dictionary, you could reverse your condition and delete values from the existing dictionary. You can't use len(count_dict) in the condition, though, and have to use another variable to keep track of the elements already "added" to (or rather, not removed from) the dictionary.
def common_words(count_dict, limit):
new_list = list(revert_dictionary(count_dict).items())[::-1]
count = 0
for number,word in new_list:
if count + len(word) > limit:
for x in word:
del count_dict[x]
else:
count += len(word)
Also note that the dict returned from revert_dictionary does not have a particular order, so the line new_list = list(revert_dictionary(count_dict).items())[::-1] is not guaranteed to give you the items in any particular order, as well. You might want to add sorted here and sort by the count, but I'm not sure if you actually want that.
new_list = sorted(revert_dictionary(count_dict).items(), reverse=True)
just write
return count_dict
below
print count_dict
in function common_words()
and change
common_words(count_dict,2)
to
count_dict=common_words(count_dict,2)
So basically you need to return value from function and store that in your variable. When you are calling function and give it a parameter. It sends its copy to that function not variable itself.

Python ranking and lists

I have this script that is suppose to rank fruits based on a number.
#! /usr/bin/python
fruits = {'Apple':1,'Banana':2,'Pineapple':3}
alist = [(v, k) for k, v in fruits.items()]
alist.sort(reverse=False)
fruit_order = str(alist).translate(None, "[]()'123456789").replace(", , ", " , ",").replace(" ", "")[1:]
print fruit_order
fruits is actully a list made up with check with .append(fruits)
#! /usr/bin/python
import re
from configobj import ConfigObj
config = ConfigObj('sites.conf')
fruits = []
substance = "juice"
target_list = config['MainConfig']['fruits']
for target in target_list:
if re.search( config[target]['Skiplist'], substance, re.M|re.I):
target = target + "'" + ":" + config[target]['Rank'] + ","
fruits.append(target)
else:
print "Dident pass"
fruits1 = "{'" + "'".join(fruits) + "}"
alist = [(v, k) for k, v in fruits1.list()]
alist.sort(reverse=False)
fruit_rank = str(alist).translate(None, "[]()'123456789").replace(", , ", ",").replace(" ", "")[1:]
print fruit_rank in example 2 prints out a line that looks exactly like test in example 1.
But iam still getting an error on example2, AttributeError: 'str' object has no attribute 'list'
And i can not really figure this one out. How would i do it?
What iam looking for is in the config file i have a rank for each fruit, the line should print out the fruits in its rank.
fruits = {'Apple': 1,'Banana': 2,'Pineapple': 3}
print ','.join(sorted(fruits, key=fruits.get))
You should use the builtin function sorted, and specify that you wish to sort by the ranks instead of by the names themselves. For your first example, here is what I would do. It sorts the dict's keys (the fruit names) by its values (their ranks).
fruits = {'Apple':1,'Banana':2,'Pineapple':3}
sorted_keys = sorted(fruits.keys(), key=lambda k: fruits[k])
Your second example is very hard to read. I would recommend that you comment code that is that complex.
It looks like you are trying to create a string containing Python code, and then execute that code. Like an eval statement. That is not good programming practice. You will want to do something like:
fruits = {} # create dict
for line in my_file.readlines():
fruit_name, rank = function_to_parse_line(line)
fruits[fruit_name] = rank
sorted_keys = sorted(fruits.keys(), key=lambda k: fruits[k])
Now there is a nice separation between the code that reads the data and the code that sorts it. If you wanted to read data from a different kind of file, you can just redefine function_to_parse_line.

Categories

Resources