Convert keys of a nested dictionary to upper case - python

Have a dictionary:
data = {'Common': {'height': 165, 'weight': 70, 'measure': ['cm', 'kg']},
'Man': 'handsome',
'Woman': {'feature': 'pretty', 'weight': 50},
'Dog': {'feature': 'barks', 'height': 10, 'weight': 20}}
Would like to convert only dictionary keys to UPPERCASE.
Tried the following code:
d = {}
d1 = {}
for k, v in data.items():
if isinstance(v, dict):
for i, j in v.items():
d1[i.upper()] = j
d[k.upper()] = d1
else:
d[k.upper()] = v
print(d)
...which produces the output with unnecessary keys and height and weight rationalization as follows:
{'COMMON': {'HEIGHT': 10, 'WEIGHT': 20, 'MEASURE': ['cm', 'kg'], 'FEATURE': 'barks'},
'MAN': 'handsome',
'WOMAN': {'HEIGHT': 10, 'WEIGHT': 20, 'MEASURE': ['cm', 'kg'], 'FEATURE': 'barks'},
'DOG': {'HEIGHT': 10, 'WEIGHT': 20, 'MEASURE': ['cm', 'kg'], 'FEATURE': 'barks'}}
My expected output is:
{'COMMON': {'HEIGHT': 165, 'WEIGHT': 70, 'MEASURE': ['cm', 'kg']},
'MAN': 'handsome',
'WOMAN': {'FEATURE': 'pretty', 'WEIGHT': 50},
'DOG': {'FEATURE': 'barks', 'HEIGHT': 10, 'WEIGHT': 20}}
Where am I going wrong?
What is the correct dictionary comprehension like {{i.upper(): j} if isinstance(j, dict) else {k.upper(): v} for k, v in data.items() for i, j in v.items()}?

You can do something like that, copy to other dict with required keys:
data = {'Common': {'height': 165, 'weight': 70, 'measure': ['cm', 'kg']},
'Man': 'handsome',
'Woman': {'feature': 'pretty', 'weight': 50},
'Dog': {'feature': 'barks', 'height': 10, 'weight': 20}}
data2 = {}
for k in data.keys():
data2[k.upper()] = data[k]
UPDATE:
If you want to change not only keys on Level 1, you should use the recursive function:
data = {'Common': {'height': 165, 'weight': 70, 'measure': ['cm', 'kg']},
'Man': 'handsome',
'Woman': {'feature': 'pretty', 'weight': 50},
'Dog': {'feature': 'barks', 'height': 10, 'weight': 20}}
def keys_to_upper(dict1):
dict2 = {}
for k in dict1.keys():
if isinstance(dict1[k], dict):
dict2[k.upper()] = keys_to_upper(dict1[k])
else:
dict2[k.upper()] = dict1[k]
return dict2
d2 = keys_to_upper(data)

The issue with your code is that you are reassigning d1 if the value is a dictionary. You can solve this by using copy.deepcopy():
Code:
from copy import deepcopy
d = {}
d1 = {}
for k, v in data.items():
if isinstance(v, dict):
for i, j in v.items():
d1[i.upper()] = j
d[k.upper()] = deepcopy(d1)
else:
d[k.upper()] = v
Output:
>>> d
{'COMMON': {'HEIGHT': 165, 'WEIGHT': 70, 'MEASURE': ['cm', 'kg']},
'MAN': 'handsome',
'WOMAN': {'FEATURE': 'pretty', 'WEIGHT': 50},
'DOG': {'FEATURE': 'barks', 'HEIGHT': 10, 'WEIGHT': 20}}
Alternatively, as a dictionary comprehension:
>>> {k.upper(): {i.upper(): j for i, j in v.items()} if isinstance(v, dict) else v for k, v in data.items()}
{'COMMON': {'HEIGHT': 165, 'WEIGHT': 70, 'MEASURE': ['cm', 'kg']},
'MAN': 'handsome',
'WOMAN': {'FEATURE': 'pretty', 'WEIGHT': 50},
'DOG': {'FEATURE': 'barks', 'HEIGHT': 10, 'WEIGHT': 20}}

List comprehension is faster because it is optimized for the Python interpreter to spot a predictable pattern during looping. Besides the syntactic benefit of list comprehensions, they are often as fast or faster than equivalent use of map .
data = {'Common': {'height': 165, 'weight': 70, 'measure': ['cm', 'kg']},
'Man': 'handsome',
'Woman': {'feature': 'pretty', 'weight': 50},
'Dog': {'feature': 'barks', 'height': 10, 'weight': 20}}
for k, v in data.items():
if isinstance(v, dict):
data[k.upper()] = data.pop(k)
output>
data = {'COMMON': {'height': 165, 'weight': 70, 'measure': ['cm', 'kg']},
'Man': 'handsome',
'WOMAN': {'feature': 'pretty', 'weight': 50},
'DOG': {'feature': 'barks', 'height': 10, 'weight': 20}}

Related

How to create a list of dictionaries from a dictionary with lists of different lengths

I want to create a list of dictionaries with the same index element from each list.
I have this dictionary:
d = {'name': ['bob', 'john', 'harry', 'mary'],
'age': [13, 19, 23],
'height': [164, 188],
'job': ['programmer']}
The desired output is:
d2 = [{'name': 'bob', 'age': 13, 'height': 164, 'job': 'programmer'},
{'name': 'john', 'age': 19, 'height': 188},
{'name': 'harry', 'age': 23},
{'name': 'mary'}]
I have tried something like this:
d2 = [dict(zip(d, t)) for t in zip(*d.values())]
But my output is:
d2 = [{'name': 'bob', 'age': 13, 'height': 164, 'job': 'programmer'}]
I think this is happening because the lists have different lengths.
You can use itertools.zip_longest and filter out None values:
from itertools import zip_longest
[{x: y for x, y in zip(d, t) if y is not None} for t in zip_longest(*d.values())]
# [{'name': 'bob', 'age': 13, 'height': 164, 'job': 'programmer'},
# {'name': 'john', 'age': 19, 'height': 188},
# {'name': 'harry', 'age': 23},
# {'name': 'mary'}]
You can use zip_longest here:
from itertools import zip_longest
keys = d.keys()
d2 = [
{k: v for k, v in zip(keys, vs) if v is not None}
for vs in zip_longest(*d.values())
]
If the values can be None as well, we can circumvent that by using a dummy value:
from itertools import zip_longest
keys = d.keys()
dummy = object()
d2 = [
{k: v for k, v in zip(keys, vs) if v is not dummy}
for vs in zip_longest(*d.values(), fillvalue=dummy)
]
Here the dummy is an object which we are sure that is not part of the items in d (since we construct it after we constructed d). By using an is comparison, we thus can know if that value was the "fillvalue".
This will give us:
>>> d2
[{'name': 'bob', 'age': 13, 'height': 164, 'job': 'programmer'}, {'name': 'john', 'age': 19, 'height': 188}, {'name': 'harry', 'age': 23}, {'name': 'mary'}]
A simple solution without using zip_longest, for the record:
d = {'name': ['bob', 'john', 'harry', 'mary'], 'age': [13, 19, 23], 'height': [164, 188], 'job': ['programmer']}
recordset = [{k: v[i] for k, v in d.items() if i < len(v)} for i in range(max([len(l) for l in d.values()]))]
print(recordset) # >> [{'name': 'bob', 'age': 13, 'height': 164, 'job': 'programmer'},
{'name': 'john', 'age': 19, 'height': 188},
{'name': 'harry', 'age': 23},
{'name': 'mary'}]
Here is another approach :
d = {'name': ['bob', 'john', 'harry', 'mary'], 'age': [13, 19, 23], 'height': [164, 188], 'job': ['programmer']}
m = max(map(len, d.values()))
d1 = {k : (v if len(v)==m else v+['']*(m-len(v))) for k,v in d.items()}
d2 = [{k:v for k,v in zip(d, t) if v} for t in zip(*d1.values())]
print(d2)
Output :
[{'height': 164, 'age': 13, 'job': 'programmer', 'name': 'bob'}, {'height': 188, 'age': 19, 'name': 'john'}, {'age': 23, 'name': 'harry'}, {'name': 'mary'}]
Just keep everything and add this import statement:
from itertools import zip_longest as zip

Combine two lists of dictionaries by value of key in dictionaries

I have two lists whose elements are dictionaries.
list1 = [
{'id': 1, 'color': 'purple', 'size': 10},
{'id': 2, 'color': 'red', 'size': 25},
{'id': 3, 'color': 'orange', 'size': 1},
{'id': 4, 'color': 'black', 'size': 100},
{'id': 5, 'color': 'green', 'size': 33}
]
list2 = [
{'id': 2, 'width': 22, 'age': 22.3},
{'id': 5, 'width': 9, 'age': 1.7}
]
I want a third list that is the same length as the larger list, and where there is a dictionary element in the smaller list that has an id that matches a dictionary element in the larger list, merge the two dictionaries, so that the final output would look like:
list3 = [
{'id': 1, 'color': 'purple', 'size': 10},
{'id': 2, 'color': 'red', 'size': 25, 'width': 22, 'age': 22.3},
{'id': 3, 'color': 'orange', 'size': 1},
{'id': 4, 'color': 'black', 'size': 100},
{'id': 5, 'color': 'green', 'size': 33, 'width': 9, 'age': 1.7}
]
Ideally if this could be done without looping over both lists, that would be ideal.
Try this nested list comprehension with a dictionary with unpacking, and a next, as well as another list comprehension:
list3 = [{**i, **next(iter([x for x in list2 if x['id'] == i['id']]), {})} for i in list1]
And now:
print(list3)
Is:
[{'id': 1, 'color': 'purple', 'size': 10}, {'id': 2, 'color': 'red', 'size': 25, 'width': 22, 'age': 22.3}, {'id': 3, 'color': 'orange', 'size': 1}, {'id': 4, 'color': 'black', 'size': 100}, {'id': 5, 'color': 'green', 'size': 33, 'width': 9, 'age': 1.7}]
Use defaultdict
from collections import defaultdict
list1 = [
{'id': 1, 'color': 'purple', 'size': 10},
{'id': 2, 'color': 'red', 'size': 25},
{'id': 3, 'color': 'orange', 'size': 1},
{'id': 4, 'color': 'black', 'size': 100},
{'id': 5, 'color': 'green', 'size': 33}
]
list2 = [
{'id': 2, 'width': 22, 'age': 22.3},
{'id': 5, 'width': 9, 'age': 1.7}
]
dict1 = defaultdict(dict)
for l in (list1, list2):
for elem in l:
dict1[elem['id']].update(elem)
list3 = dict1.values()
print(list(list3))
O/P:
[
{
'id': 1,'color': 'purple','size': 10
},
{
'id': 2,'color': 'red','size': 25,'width': 22,'age': 22.3
},
{
'id': 3,'color': 'orange','size': 1
},
{
'id': 4,'color': 'black','size': 100
},
{
'id': 5, 'color': 'green','size': 33, 'width': 9,'age': 1.7
}
]
list3 is not guaranteed to be sorted (.values() returns items in no
specific order, you can try this to sort.
from operator import itemgetter
...
new_list = sorted(dict1.values(), key=itemgetter("id"))

Partitioning a List of Dictionaries in Python According to a Value

I have a coding challenge requires me to create a logic that partitions a list of dictionaries into three new lists of dicts. The new lists need to have the same number of experienced and inexperienced personnel. The original list has an even number of experienced and inexperienced personnel. I have no idea how to form the logic for this challenge. Here is a shortened version:
mylist = [
{'name': 'Jade', 'height': 64, 'experience': 'n'},
{'name': 'Diego', 'height': 60, 'experience': 'y'},
{'name': 'Twee', 'height': 70, 'experience': 'n'},
{'name': 'Wence', 'height': 72, 'experience': 'y'},
{'name': 'Shubha', 'height': 65, 'experience': 'y'},
{'name': 'Taylor', 'height': 68, 'experience': 'n'}
]
The new dicts need to have equal numbers of experienced and inexperienced personnel like this:
newlist_1 = [
{'name': 'Diego', 'height': 60, 'experience': 'y'},
{'name': 'Jade', 'height': 64, 'experience': 'n'},
]
newlist_2 = [
{'name': 'Wence', 'height': 72, 'experience': 'y'},
{'name': 'Twee', 'height': 70, 'experience': 'n'},
]
newlist_3 = [
{'name': 'Shubha', 'height': 65, 'experience': 'y'},
{'name': 'Taylor', 'height': 68, 'experience': 'n'}
]
I am keeping the original list, so in the end there needs to be a total of four collections.
def make_teams(my_list):
# divide the member list in two
experienced = list()
novice = list()
for record in my_list:
if record.get('experience') in ['Y','y']:
experienced.append(record)
else:
novice.append(record)
# stitch the two lists together as a list of tuples
teams = zip(experienced, novice)
# build a dictionary result starting with the member list
results={
'members':my_list
}
# update results with each team
for i in range(0,len(teams)):
results.update(
{'newlist_%s'%(i+1):list(teams[i])})
return results
Will produce the following...
from pprint import pprint
pprint(make_teams(mylist))
{'members': [{'experience': 'n', 'height': 64, 'name': 'Jade'},
{'experience': 'y', 'height': 60, 'name': 'Diego'},
{'experience': 'n', 'height': 70, 'name': 'Twee'},
{'experience': 'y', 'height': 72, 'name': 'Wence'},
{'experience': 'y', 'height': 65, 'name': 'Shubha'},
{'experience': 'n', 'height': 68, 'name': 'Taylor'}],
'newlist_1': [{'experience': 'y', 'height': 60, 'name': 'Diego'},
{'experience': 'n', 'height': 64, 'name': 'Jade'}],
'newlist_2': [{'experience': 'y', 'height': 72, 'name': 'Wence'},
{'experience': 'n', 'height': 70, 'name': 'Twee'}],
'newlist_3': [{'experience': 'y', 'height': 65, 'name': 'Shubha'},
{'experience': 'n', 'height': 68, 'name': 'Taylor'}]}
You can have 2 lists - one with experienced and one with inexperienced and build whatever lists you need from that, something like:
experienced = [worker for worker in mylist if 'y' == worker['experience']]
inexperienced = [worker for worker in mylist if 'n' == worker['experience']]
list1, list2, list3 = map(list, zip(experienced, inexperienced))
Try slicing the list of dictionaries into three seperate list
list1 = mylist[0:2]
list2 = mylist[2:4]
list3 = mylist[4:6]

How to select a sub-dictionary given a condition

mydict = {'a': {'name': 'Marco', 'gender': 'm', 'age': 38, 'info': 'teacher musician'}
'b': {'name': 'Daniela', 'gender': 'f', 'age': 28, 'info': 'student music'}
'c': {'name': 'Maria', 'gender': 'f', 'age': 25, 'info': 'doctor dance whatever'}}
How to select the records with an age below 30 and with the words including 'music' in the 'info'?
The results should be like:
newdict = {'b': {'name': 'Daniela', 'gender': 'f', 'age': 28, 'info': 'student music'}}
Simplest way is to use a dict-comp:
mydict = {'a': {'name': 'Marco', 'gender': 'm', 'age': 38, 'info': 'teacher musician'},
'b': {'name': 'Daniela', 'gender': 'f', 'age': 28, 'info': 'student music'},
'c': {'name': 'Maria', 'gender': 'f', 'age': 25, 'info': 'doctor dance whatever'}}
new_dict = {k:v for k,v in mydict.iteritems() if v['age'] < 30 and 'music' in v['info'].split()}
# {'b': {'info': 'student music', 'gender': 'f', 'age': 28, 'name': 'Daniela'}}
You can use the following comprehension :
>>> {d:k for d,k in mydict.items() if k['age']<30 and 'music' in k['info']}
{'b': {'info': 'student music', 'gender': 'f', 'age': 28, 'name': 'Daniela'}}
mydict.items() give you a tuple contain key ans value of dictionary at each loop , and you can chose the item that have the proper conditions !

Removing common elements from a dictionary of lists in python

I have a dictionary of lists and the lists contain dictionaries like so:
my_dict = {
'list1': [{'catch': 100, 'id': '1'}, {'catch': 101, 'id': '2'},
{'catch': 50, 'id': '1'}],
'list2': [{'catch': 189, 'id': '1'}, {'catch': 120, 'id': '12'}],
'list3': [{'catch': 140, 'id': '1'}, {'catch': 10, 'id': '100'}]
}
What is the most pythonic way of removing the list items with commin 'id' values and storing them in a separate list? So the output would be something like this:
my_dict = {
'list1': [{'catch': 101, 'id': '2'}],
'list2': [{'catch': 120, 'id': '12'}],
'list3': [ {'catch': 10, 'id': '100'}],
'list4': [{'catch': 100, 'id': '1'}, , {'catch': 50, 'id': '1'},
{'catch': 189, 'id': '1'}, {'catch': 140, 'id': '1'}]
}
In my program I have 7 lists similar to this, and if an 'id' appears in two or more of these lists, I want to store all appearances of an item with that 'id' in the 8th list for further processing
with regards,
finnurtorfa
Consider restructuring your data into something like this:
>>> import itertools
>>> { k: [d['catch'] for d in v] for k, v in itertools.groupby(sorted(itertools.chain(*my_dict.itervalues()), key=lambda d: d['id']), lambda d: d['id']) }
{'1': [100, 50, 140, 189], '2': [101], '100': [10], '12': [120]}
You haven't described what your data represents, so this may not be appropriate for you. But the tools used (chain and groupby from itertools) should at least give you some ideas.
Edit: I used the sample answer from the question in my testing by accident. Fixed by adding sorting to the input to groupby.
>>> get_id = operator.itemgetter("id")
>>> flattened_dict = itertools.chain.from_iterable(my_dict.values())
>>> groups = itertools.groupby(sorted(flattened_dict, key=get_id), get_id)
>>> {k: list(v) for k, v in groups}
{'1': [{'catch': 100, 'id': '1'},
{'catch': 50, 'id': '1'},
{'catch': 140, 'id': '1'},
{'catch': 189, 'id': '1'}],
'100': [{'catch': 10, 'id': '100'}],
'12': [{'catch': 120, 'id': '12'}],
'2': [{'catch': 101, 'id': '2'}]}
Explanation:
get_id is a function that takes an object x and returns x["id"].
flattened_dict is just an iterable over all the lists (i.e. concatenating all the .values() of my_dict
Now we sort flattened_dict with the key function get_id -- that is, sort by ID -- and group the result by id.
This basically works because itertools.groupby is awesome.
Something along the following line:
my_dict = {
'list1': [{'catch': 100, 'id': '1'}, {'catch': 101, 'id': '2'},
{'catch': 50, 'id': '1'}],
'list2': [{'catch': 189, 'id': '1'}, {'catch': 120, 'id': '12'}],
'list3': [{'catch': 140, 'id': '1'}, {'catch': 10, 'id': '100'}]
}
from itertools import groupby
sub = {}
for k in my_dict:
for kk, g in groupby( my_dict[k], lambda v: v["id"] ):
if not kk in sub:
sub[kk] = []
sub[kk] = sub[kk] + list( g )
print sub
{'1': [{'catch': 100, 'id': '1'}, {'catch': 50, 'id': '1'}, {'catch': 140, 'id': '1'}, {'catch': 189, 'id': '1'}], '12': [{'catch': 120, 'id': '12'}], '100': [{'catch': 10, 'id': '100'}], '2': [{'catch': 101, 'id': '2'}]}

Categories