Update keys in nested dictionary with some exception in key strings - python

So I've a list of students which looks something like this :
students = [ {'name': 'Jack' , 'status' : 'Average' , 'subjects' : { 'subject1' : 'English' , 'subject2' : 'Math' } , 'height' : '20cm' },
{'name': 'Tom' , 'status' : 'Good' , 'subjects' : { 'subject1' : 'English' , 'subject2' : 'Science' } , 'height' : '30cm' }
]
So the above list is of size 2. Assume that the size is pretty big, lets say 50 or 60 or more.
I want to return a list students_output & for each student I want to return a dictionary which contains the following values for each student which are fetched from the above list but have slightly modified 'keys'. The end output should be something like this :
students_output = [ {'student_name': 'Jack' , 'student_status' : 'Average' , 'student_subjects' : { 'student_subject1' : 'English' , 'student_subject2' : 'Math' } , 'child_height' : '20cm' },
{'student_name': 'Tom' , 'student_status' : 'Good' , 'student_subjects' : { 'student_subject1' : 'English' , 'student_subject2' : 'Science' } , 'child_height' : '30cm' }
]
I am not able to understand how I can create an effective loop so that the keys in my resultant data structure are maintained as provided in the output and i can fetch the data from the first list.
for example, in students_output, I know
students_output[0]['student_name']=students[0]['name']
But can anyone help me do it iteratively ?

In order to achieve this, you have to concatenate "student_" at the start of each key with some exception as "height" key. You may do it via combination of list comprehension and dict comprehension expression as:
students = [
{'name': 'Jack' , 'status' : 'Average' , 'subjects' : { 'subject1' : 'English' , 'subject2' : 'Math' } , 'height' : '20cm' },
{'name': 'Tom' , 'status' : 'Good' , 'subjects' : { 'subject1' : 'English' , 'subject2' : 'Science' } , 'height' : '30cm' }
]
def get_key(key):
return {
'height': 'child_height', # All exception you need in `key`
# apart from concatenating `"student_"`
}.get(key, 'student_' + key)
new_list = [{
get_key(k): ({
get_key(kk):v for kk, vv in v.items()} if isinstance(v, dict) else v) \
for k, v in s.items()
} for s in students]
Value hold by new_list will be:
[{'student_name': 'Jack', 'child_height': '20cm', 'student_status': 'Average', 'student_subjects': {'student_subject1': {'subject1': 'English', 'subject2': 'Math'}, 'student_subject2': {'subject1': 'English', 'subject2': 'Math'}}},
{'student_name': 'Tom', 'child_height': '30cm', 'student_status': 'Good', 'student_subjects': {'student_subject1': {'subject1': 'English', 'subject2': 'Science'}, 'student_subject2': {'subject1': 'English', 'subject2': 'Science'}}}]

Here's a quick-and-dirty function that will do what you need:
In [10]: def rename_keys(students):
...: d = {}
...: for k,v in students.items():
...: if isinstance(v,dict):
...: k = "student_" + k
...: v = rename_keys(v)
...: d[k] = v
...: elif k == 'height':
...: k = "child_height"
...: d[k] = v
...: else:
...: k = "student_" + k
...: d[k] = v
...: return d
...:
...:
In [11]: [rename_keys(d) for d in students]
Out[11]:
[{'child_height': '20cm',
'student_name': 'Jack',
'student_status': 'Average',
'student_subjects': {'student_subject1': 'English',
'student_subject2': 'Math'}},
{'child_height': '30cm',
'student_name': 'Tom',
'student_status': 'Good',
'student_subjects': {'student_subject1': 'English',
'student_subject2': 'Science'}}]
And really, this doesn't have to be recursive, you could substitute the recursive call with a dictionary comprehension:
v = {'student_'+key:value for key,value in v.items()}

You can use the following function inside a list comprehension like this:
def new_dict(d):
res = {}
for key, value in d.iteritems():
student_or_child = 'student' if key != 'height' else 'child'
if type(value) == dict:
res['{}_{}'.format(student_or_child, key)] = new_dict(value)
else:
res['{}_{}'.format(student_or_child, key)] = value
return res
The above function takes a dict as argument, for each key, value in the passed dict, if value is of type dict then the same function is called on value, and the result is added to res dict, else the same value is added.
Now, with a list comprehension, we can do:
[new_dict(d) for d in students]
Output:
>>> [new_dict(d) for d in students]
[{'child_height': '20cm', 'student_name': 'Jack', 'student_status': 'Average', 'student_subjects': {'student_subject1': 'English', 'student_subject2': 'Math'}}, {'child_height': '30cm', 'student_name': 'Tom', 'student_status': 'Good', 'student_subjects': {'student_subject1': 'English', 'student_subject2': 'Science'}}]

Related

Python how to parse a list[dict] in python and convert values from nested dictionaries as keys

Need help in writing clean code , I have a yaml parsed output which looks like this :
yaml_output = [{'name' : 'alex', 'subjects' : {'maths' : ['grade_1', 'grade_2']}},
{'name' : 'rio', 'subjects' : {'maths' : ['grade_3', 'grade_2'], 'science : ['grade_4', 'grade_6']}}]
I want it create a list of dictionaries with subjects as key and name of the students as value in a dictionary where grades are the keys.
desired_output = [{'maths' : {'grade_1' : ['alex'], 'grade_2' : ['alex', 'rio'], 'grade_3' : ['rio']}}, {'science' : {'grade_4' : ['rio'], 'grade_6' : ['rio']}
needs subjects as key and name of the students as value in a dictionary where grades are the keys.
new_dict = []
for dict in yaml_output:
for k,v in dict:
for i,j in dict['subjects']:
if any(i in dict_list for dict_list in new_dict):
dict_list[i].append(v)
You were close. Your for k,v loop is looking at the wrong data. You don't want to look at ALL the keys, you want to unravel the subjects key and reference the "name" specifically.
yaml_output = [{'name' : 'alex', 'subjects' : {'maths' : ['grade_1', 'grade_2']}},
{'name' : 'rio', 'subjects' : {'maths' : ['grade_3', 'grade_2'], 'science': ['grade_4', 'grade_6']}}]
out = dict()
for data in yaml_output:
for k,v in data['subjects'].items():
if k not in out:
out[k] = {}
for g in v:
if g not in out[k]:
out[k][g] = []
out[k][g].append( data['name'] )
print(out)
Output:
{'maths': {'grade_1': ['alex'], 'grade_2': ['alex', 'rio'], 'grade_3': ['rio']}, 'science': {'grade_4': ['rio'], 'grade_6': ['rio']}}

How to conditionally select elements in a list comprehension?

I couldn't find any examples that match my use case. Still working through my way in python lists and dictionaries.
Problem:
all_cars = {'total_count': 3,'cars': [{'name': 'audi','model': 'S7'}, {'name': 'honda', 'model': 'accord'},{'name': 'jeep', 'model': 'wrangler'} ]}
owners = {'users':[{'owner': 'Nick', 'car': 'audi'},{'owner': 'Jim', 'car': 'ford'},{'owner': 'Mike', 'car': 'mercedes'} ]}
def duplicate():
for c in all_cars['cars']:
if c['name'] == [c['users']for c in owners['users']]:
pass
else:
res = print(c['name'])
return res
output = ['honda', 'jeep', audi']
and
def duplicate():
for c in all_cars['cars']:
if c['name'] == 'audi':
pass
else:
res = print(c['name'])
return res
output - ['honda', 'jeep']
I am trying to find matching values in both dictionaries, using list comprehension, then return non-matching values only.
Solution: Using 'in' rather than '==' operator, I was able to compare values between both lists and skip duplicates.
def duplicate():
for c in all_cars['cars']:
if c['name'] in [c['users']for c in owners['users']]:
pass
else:
res = print(c['name'])
return res
To answer the question in your title, you can conditionally add elements during a list comprehension using the syntax [x for y in z if y == a], where y == a is any condition you need - if the condition evaluates to True, then the element y will be added to the list, otherwise it will not.
I would just keep a dictionary of all of the owner data together:
ownerData = { "Shaft" : {
"carMake" : "Audi",
"carModel" : "A8",
"year" : "2015" },
"JamesBond" : {
"carMake" : "Aston",
"carModel" : "DB8",
"year" : "2012" },
"JeffBezos" : {
"carMake" : "Honda",
"carModel" : "Accord"
"year" : "1989"}
}
Now you can loop through and query it something like this:
for o in ownerData:
if "Audi" in o["carMake"]:
print("Owner %s drives a %s %s %s" % (o, o["year"], o["carMake"], o["carModel"]))
Should output:
"Owner Shaft drives a 2015 Audi A8"
This way you can expand your data set for owners without creating multiple lists.
OK, based on your feedback on the solution above, here is how I would tackle your problem. Drop your common items into lists and then use "set" to print out the diff.
all_cars = {'total_count': 3,'cars': [{'name': 'audi','model': 'S7'},
{'name': 'honda', 'model': 'accord'},{'name': 'jeep', 'model': 'wrangler'} ]}
owners = {'users':[{'owner': 'Nick', 'car': 'audi'},{'owner': 'Jim',
'car': 'ford'},{'owner': 'Mike', 'car': 'mercedes'} ]}
allCarList = []
ownerCarList = []
for auto in all_cars['cars']:
thisCar = auto['name']
if thisCar not in allCarList:
allCarList.append(thisCar)
for o in owners['users']:
thisCar = o['car']
if thisCar not in ownerCarList:
ownerCarList.append(thisCar)
diff = list(set(allCarList) - set(ownerCarList))
print(diff)
I put this in and ran it and came up with this output:
['jeep', 'honda']
Hope that helps!

Filtering dict of dict with dictionary comprehension

I have problem with filtering dict of dict while using dict comprehension.
I have dict:
clients = {
'Shop': {'url' : 'url_v', 'customer' : 'cumoster_v',
'some_other_key1' : 'some_value'},
'Gym': {'url' : 'url_v1', 'customer_1' : 'customer_v1', 'customer_2': 'customer_v2',
'some_other_key2' : 'some_value'},
'Bank': {'url' : 'url_v2', 'customer_3' : 'customer_v3',
'some_other_key3' : 'some_value'}
}
I would like to make another dict which will have only 'customer.*' keys.
So, new dict should looks like:
dict_only_cust = {
'Shop': {'customer' : 'cumoster_v'},
'Gym': {'customer_1' : 'customer_v1', 'customer_2': 'customer_v2'},
'Bank': {'customer_3' : 'customer_v3'}
}
As I'm big fan of lists and dicts comprehension, I'm wondering if it is possible to do it with this.
So far, I've written:
dict_only_cust = {v.pop(t) for k, v in clients.items()
for t, vv in v.items()
if not re.match('.*customer.*', t)}
Code fails with 'RuntimeError: dictionary changed size during iteration'
Second time I've tried:
dict_only_cust = {k:{t: vv} for k, v in clients.items()
for t, vv in v.items()
if re.match('.*customer.*', t)}
It is almost OK, but it is returning
dict_only_cust = {
'Shop' : {'customer' : 'cumoster_v'},
'Gym' : {'customer_1' : 'customer_v1'},
'Bank' : {'customer_3' : 'customer_v3'}
}
How to solve this problem using dict comprehension?
I'm using python 3.4.
Thanks!
>>> {key:{k:v for k,v in dic.items() if 'customer' in k} for key,dic in clients.items()}
{'Shop': {'customer': 'cumoster_v'}, 'Gym': {'customer_2': 'customer_v2', 'customer_1': 'customer_v1'}, 'Bank': {'customer_3': 'customer_v3'}}
{k:{k1:v1 for k1,v1 in v.items() if k1.startswith('customer')} for k, v in clients.items()}
Output :
{'Shop': {'customer': 'cumoster_v'}, 'Gym': {'customer_2': 'customer_v2', 'customer_1': 'customer_v1'}, 'Bank': {'customer_3': 'customer_v3'}}

How to replace a Value in a Dict from another Dict but based on a key value in the first Dict

For say I have two dictionaries like so:
dict1 = {'project' : 'Administration', 'cost_center' : 'N/A'}
{'project' : 'Non-Work Time', 'cost_center' : 'N/A'}
dict2 = {'project':'Administration', 'cost_center': 'F99'}
{'project':'Non-Work Time', 'cost_center': 'F88'}
I need help finding a way to replace 'N/A' with the value from dict2 cost_center. I think an IF statement would be best like: if it finds "N/A" then replace with the value that is in dict2 if it doesn't find that value keep it "N/A"
Example of desired output:
dict1 = {'project' : 'Administration', 'cost_center' : 'F99'}
{'project' : 'Non-Work Time', 'cost_center' : 'F88'}
Pseudo:
if dict1['cost_center'] == "N/A':
Replace 'cost' if 'project' == exists in dict2
else:
continue
You can use a dict comprehension :
>>> dict1 = {'project' : 'Non-Work Time', 'cost' : 'N/A'}
>>> dict2 = {'Administration': 'F99' , 'Non-Work Time': 'Q99'}
>>> dict1 = {i:dict2[dict1['project']] if i=='cost' and j=='N/A' else j for i,j in dict1.items()}
>>> dict1
{'project': 'Non-Work Time', 'cost': 'Q99'}
But note that your dictionaries must contain the expected keys in otherwise it will raise a KeyError.

Python how to compare string in a list with dict

I have a code like this:
It will print Student
d= u'pen hahahahaha'
area = [u'Apple',u'Banana',u'lemon']
area2 = [ u'pen',u'book',u'chair' ]
area3 = [u'father',u'mother']
if any(d.startswith(i) for i in area):
category = 'Fruit'
print 'Fruit'
elif any(d.startswith(i) for i in area2):
category = 'Student'
print 'Student'
elif any(d.startswith(i) for i in area3):
category = 'family'
print 'family'
I want to know how to edit it to a mode like this:
aa = [{"Fruit":[u'Apple',u'Banana',u'lemon']},
{"Student":[ u'pen',u'book',u'chair' ]},
{"Family":[u'father',u'mother']}]
So I can compare if 'pen hahahahaha' in {"Student":[ u'pen',u'book',u'chair' ]}
save category = 'Student'
I think for a while but have no idea,please guide me.Thank you
You can use loop:
categories = {
"Fruit": [u'Apple', u'Banana', u'lemon'],
"Student": [u'pen', u'book', u'chair'],
"Family": [u'father', u'mother']
}
def get_category(value):
for cat, cat_entries in categories.iteritems():
for cat_entry in cat_entries:
if value.startswith(cat_entry):
return cat
return None
print get_category('pen hahahahaha')
Output:
Student
Make aa a dictionary like:
aa = {"Fruit":[u'Apple',u'Banana',u'lemon'],
"Student":[ u'pen',u'book',u'chair' ],
"Family":[u'father',u'mother']}
obj = 'pen'
for key in aa:
if obj in aa[key]:
print(obj + ' is in ' + key)
edit:
May be this will suit your requirement more
aa = {"Fruit":[u'Apple',u'Banana',u'lemon'],
"Student":[ u'pen',u'book',u'chair' ],
"Family":[u'father',u'mother']}
obj = u'pen hahhah'
for key in aa:
for item in aa[key]:
if obj.startswith(item):
print(obj + ' is in ' + key)
aa = [{"Fruit":[u'Apple',u'Banana',u'lemon']},
{"Student":[ u'pen',u'book',u'chair' ]},
{"Family":[u'father',u'mother']}]
d=u'pen haaaaaa'
print [ x.keys()[0] for x in aa for y in x.values()[0] if y in d.split() ]

Categories

Resources