Python dictionary to dictionary mapping - python

I'm looking for recursion dynamic function/library to mapping dictionary with specific keys.
Example
my_data = {
'name': 'Test',
'primary_address': {
'full_address': 'Address test',
},
'other_field': 'Other field'
}
mapping_keys = {
'name': 'full_name',
'primary_address__full_address': 'primary_address__address'
}
Expected result
{
'full_name': 'Test',
'primary_address': {
'address': 'Address test',
}
}
What I tried, but it doesn't work with recursion
def mapping_data(json_data, mapping_keys):
mapped_data = []
for data in json_data:
mapped_data_tmp = {}
for key in data:
if key in mapping_keys:
mapped_data_tmp.update({mapping_keys[key]: data[key]})
mapped_data.append(mapped_data_tmp)
return mapped_data

from functools import reduce
import operator
my_data = {
'name': 'Test',
'primary_address': {
'full_address': 'Address test',
},
'other_field': 'Other field'
}
mapping_keys = {
'name': 'full_name',
'primary_address__full_address': 'primary_address__address'
}
def get_item_by_path(data,path):
return reduce(operator.getitem,path,data)
def set_item_by_path(data, path, value):
try:
get_item_by_path(data, path[:-1])[path[-1]] = value
except KeyError:
for key in reversed(path):
value = {key: value}
data[path[0]] = value[path[0]]
def mapKeys(data, mapping_keys):
result = {}
for oldkey in mapping_keys:
oldkeys = oldkey.split('__')
val = get_item_by_path(my_data, oldkeys)
newkeys = mapping_keys[oldkey].split('__')
set_item_by_path(result, newkeys, val)
return result
newDict = mapKeys(my_data, mapping_keys)

def mapping_data(json_data, mapping_keys, prefix=''):
mapped_data = {}
for key, val in json_data.items():
map_key = (prefix + '__' + key) if prefix else key
if map_key in mapping_keys:
key = mapping_keys[map_key].rsplit('__', 1)[-1]
mapped_data[key] = val
if isinstance(val, dict):
val = mapping_data(val, mapping_keys, map_key)
mapped_data[key] = val
return mapped_data
res = mapping_data(my_data, mapping_keys)
print (res)
Output
{
"full_name": "Test",
"primary_address": {
"address": "Address test"
}
}

Related

String array to nested dictionary Python

I'm a begginer in python and I want to make this:
I have a string array I want to make a dictionary with string as keys, but this way:
Transform this:
['Users', 'ID', 'Age']
Into this:
{
'Users': {
'ID': {
'Age': None
}
}
}
You could do this like so:
def tranform_list_to_dict(lst):
new = {}
for item in lst[::-1]:
if not new:
new[item] = None
else:
tmp = {}
tmp[item] = new.copy()
new = dict(tmp)
return new
my_list = ['Users', 'ID', 'Age']
print(tranform_list_to_dict(my_list))
Which will produce:
{
"Users": {
"ID": {
"Age": None
}
}
}
you may do this
list1 = ['User', 'ID', 'Age']
def transform(alist):
alist = alist[::-1]
dic = {alist[0]: None}
for i in range(len(alist)-1):
dic = {alist[i]: dic}
return dic
print(transform(list1))

How to access existing dict key inside a for loop without creating a new key?

I have a list of dictionaries like so
names = [{'id':1, 'name': 'Alice', 'dep_name':'Pammy', 'is_dep_minor':True, 'is_insured':False},
{'id':2, 'name': 'Alice', 'dep_name':'Trudyl', 'is_dep_minor':False, 'is_insured':True},
{'id':3, 'name': 'Bob', 'dep_name':'Charlie', 'is_dep_minor':True, 'is_insured':True},]
I want to create a new unified dictionary with new properties that will be populated later. I am planning to eliminate the need to have two dicts for Alice when it can be nested inside. This is what I have so far.
results = []
for name in names:
newdict = defaultdict(dict)
newdict[name[name]]["NEWKEY"] = None # to be filled in later
newdict[name[name]]["ANOTHERKEY"] = None # to be filled in later
innerdict = defaultdict(dict)
innerdict["dep_name"]["is_minor"] = name["is_dep_minor"]
innerdict["dep_name"]["is_insured"] = name["is_insured"]
newdict[name[name]]["DEPENDENTS"] = innerdict
results.append(newdict)
This gives me
[
{
"Alice" : {
"NEWKEY" : None,
"ANOTHERKEY" : None,
"DEPENDENTS" : {
"Pammy" : {
"is_minor" : True,
"is_insured" : False
}
}
}
},
{
"Alice" : {
"NEWKEY" : None,
"ANOTHERKEY" : None,
"DEPENDENTS" : {
"Trudy" : {
"is_minor" : False,
"is_insured" : True
}
}
}
},
# and the list goes on
]
What I'm aiming for is
{
"Alice" : {
"NEWKEY" : None,
"ANOTHERKEY" : None,
"DEPENDENTS" : {
"Pammy" : {
"is_minor" : True,
"is_insured" : False
},
"Trudy" : {
"is_minor" : False,
"is_insured" : True
}
}
}
},
Can someone help me out with this? Thanks in Advance
I found a solution to my problem. I did it like so
results = {}
def create_dict():
newdict = {}
newdict["NEWKEY"] = None # to be filled in later
newdict["ANOTHERKEY"] = None # to be filled in later
innerdict = defaultdict(dict)
innerdict["dep_name"]["is_minor"] = name["is_dep_minor"]
innerdict["dep_name"]["is_insured"] = name["is_insured"]
newdict["DEPENDENTS"] = innerdict
return newdict
for name in names:
if name["name"] in results.keys():
dname = name["dep_name"]
is_minor = name["is_dep_minor"]
is_insured = name["is_dep_insured"]
name = results.get(name["name"])
name["DEPENDENT"][dname]["is_dep_minor"] = is_minor
name["DEPENDENT"][dname]["is_dep_insured"] = is_insured
else:
newdict = create_dict()
results[name["name"]] = newdict
This thing gives the desired output

Python Group items in dictionary based on value only if they are successive?

I've been banging my head to group the successive items in the dictionary based on the value.
Suppose this is my data:
data = {
12: {
type: 'image'
},
13: {
type: 'image'
},
1: {
type: 'audio'
},
2: {
type: 'image'
},
5: {
type: 'image'
}
}
I want to group similar items together only they are coming in succession, like this:
group = {
12: {
type: 'image',
items: [12, 13]
},
1: {
type: 'audio'
},
2: {
type: 'image',
items: [2, 5]
}
}
This is what I've tried:
successive = False
last_key = -1
for key, item in data.items():
if item['type'] == 'image':
if not successive: # use a new key
last_key = key
if last_key in group:
group[last_key]['items'].append(item)
else:
group[last_key] = {
'type': 'image',
'items': list()
}
group[last_key]['items'].append(item)
successive = True
else:
group[key] = item
successive = False
But it is grouping all of the images inside just one list. I am really confused where did I miss in my logic?
Try this (My OCD made me change the dictionary keys to strings)
def format(data):
last_type = None
last_key = -1
formatted = {}
for key, item in data.items():
if item[type] != last_type:
formatted[key] = {'type': 'image', 'items': [key]}
last_key = key
last_type = item[type]
else:
formatted[last_key]['items'].append(key)
last_key = key
last_type = item[type]
return formatted
Try this solution out. For any groups with only one item, then the length of items will just be 1.
from copy import deepcopy
currentType = list(data.values())[0]['type']
currentVal = list(data.keys())[0]
currentItems = [list(data.keys())[0]]
res = {}
for k,v in data.items():
if k not in res and k == currentVal:
res[currentVal] = {'type': v['type'], 'items' : deepcopy(currentItems)}
elif k not in res and v['type'] == currentType:
res[currentVal]['items'].append(k)
else:
currentItems.clear()
currentVal = k
currentType = data[k]['type']
currentItems.append(k)
res[currentVal] = {'type': currentType, 'items' : deepcopy(currentItems)}
print(res)

Remove keys from a nested dict (Python keys)

I'm pretty new in Python, thanks in advance for your help.
I built the following code (I tried the below, I used a dictionary within a dictionary).
The idea is to keep the keys (hair.color) with values(blonde). In this example: remove Micheal.
Code:
def answers(hair_questions):
try:
for i in people:
if people[i]["hair.color"]==hair_questions:
print(people[i])
else:
del people[i]
return people[i]
except:
print("Doesn´t exist")
answers("brown")
On People:
people={
"Anne":
{
"gender":"female",
"skin.color":"white",
"hair.color":"blonde",
"hair.shape":"curly"
}
,
"Michael":
{
"citizenship":"africa",
"gender":"male",
"hair.color":"brown",
"hair.shape":"curly"
}
,
"Ashley":
{
"gender":"female",
"citizenship":"american",
"hair.color":"blonde",
"hair.shape":"curly "
}
}
The code only check the first key: under the condition: values(blonde) i.e. (people[i]["hair.color"]!=brown) it works just for 1 key and then the code gets "stuck"
My current output:
"people"=
"Michael":
{
"citizenship":"africa",
"gender":"male",
"hair.color":"brown",
"hair.shape":"curly"
}
,
"Ashley":
{
"gender":"female",
"citizenship":"american",
"hair.color":"blonde",
"hair.shape":"curly "
}
Instead, I wanted:
"people"=
"Michael":
{
"citizenship":"africa",
"gender":"male",
"hair.color":"brown",
"hair.shape":"curly"
}
I want an output, for this case, (only) Michael.
You can't delete key while iterating for loop:
people={
"Anne":
{
"gender":"female",
"skin.color":"white",
"hair.color":"blonde",
"hair.shape":"curly"
},
"Michael":
{
"citizenship":"africa",
"gender":"male",
"hair.color":"brown",
"hair.shape":"curly"
},
"Ashley":
{
"gender":"female",
"citizenship":"american",
"hair.color":"blonde",
"hair.shape":"curly "
}
}
def answers(hair_questions):
my_dict = {}
for i in people:
if people[i]["hair.color"] in hair_questions:
my_dict[i] = people[i]
return my_dict
print(answers("brown"))
OR
def answers(hair_questions):
my_list = []
for i in people:
if people[i]["hair.color"] not in hair_questions:
my_list.append(i)
for i in my_list:
del people[i]
answers("brown")
print(people)
O/P:
{'Michael': {'citizenship': 'africa', 'gender': 'male', 'hair.color': 'brown', 'hair.shape': 'curly'}}
you can use list comprehension:
brown = {key:value for key,value in people.items() if people[key]["hair.color"] != "blonde"}
print (brown)
what is equal to:
brown= {}
for key,value in people.items():
if people[key]["hair.color"] != "blonde":
brown[key] = value
print (brown)
output:
{'Michael': {'citizenship': 'africa', 'gender': 'male', 'hair.color': 'brown', 'hair.shape': 'curly'}}

aggregating values in dictionary

I have a deep dictionary like this:
myDict = { '123456': {
'348adbd39r' : {
'LONDON': {
'c_name': 'abc',
'acct': '84720'
},
'PARIS': {
'c_name': 'xyz',
'acct': '73642'
}
},
'2862aef3' : {
'NYC': {
'c_name': 'hhdls3',
'acct': '92742'
}
},
'82gfg24' : {
'NYC': {
'c_name': 'hquer',
'acct': '34567'
},
'PARIS': {
'c_name': 'ljad',
'acct': '93742'
}
}
}
I want to 'aggregate' it based on the city names. The output should look like below:
outDict = {
'LONDON': {
'c_name': ['abc'],
'acct': ['84720']
},
'PARIS': {
'c_name': ['xyz', 'ljad'],
'acct': ['73642', '93742']
},
'NYC': {
'c_name': ['hhdls3', 'hquer'],
'acct': ['73642', '34567']
}
}
This is what I did:
cust_fields = ['c_name', 'acct']
field_dict = {field: [] for field in cust_fields}
aggregated_dict = {}
city_names = ['LONDON', 'PARIS', 'NYC']
for city in city_names:
aggregated_dict[city] = field_dict
for id, an_dict in myDict.iteritems():
for alphaNum, city_dict in an_dict.iteritems():
for city, acct_dict in city_dict.iteritems():
for field, val in acct_dict.iteritems():
aggregated_dict[city][field].append(val)
But, the above is updating the field-values for all the cities...rather than just the particular city it is working on. Not sure where the logic is wrong. Any help is appreciated (either correct where my mistake is or any new logic...).
Thanks!
The problem you have is you are assigning field_dict to the value of aggregated_dict[city] in your loop over city_names, which is simply assigning the same dictionary to each city. And when you update any reference (for any city) all references are updated.
An easy fix for this is to change
for city in city_names:
aggregated_dict[city] = field_dict
To:
for city in city_names:
aggregated_dict[city] = {field: [] for field in cust_fields}
I would also look at collections.defaultdict for this type of aggregation.
from collections import defaultdict
collected = defaultdict(lambda: defaultdict(list))
for _, city_records in myDict['123456'].items():
for city_name, records in city_records.items():
for record_name, record_value in records.items():
collected[city_name][record_name].append(record_value)
for city_name, records in collected.items():
print city_name
print dict(records)
for key,val in myDict.items():
for key1,val1 in val.items():
for key2,val2 in val1.items():
d = final_dict[key2] if key2 in final_dict else defaultdict(list)
for k,v in val2.items():
d[k].append(v)
final_dict[key2] = d
You can use recursion:
from collections import defaultdict
d1 = defaultdict(dict)
def aggregate(d):
for a, b in d.items():
if a in ['LONDON', 'PARIS', 'NYC']:
global d1
if a not in d1:
d1[a] = {}
d1[a]['c_name'] = [b['c_name']]
d1[a]['acct'] = [b['acct']]
else:
d1[a]['c_name'].append([b['c_name']])
d1[a]['acct'].append(b['acct'])
else:
aggregate(b)
aggregate(myDict)
print(dict(d1))
Output:
{'PARIS': {'acct': ['73642', '93742'], 'c_name': ['xyz', ['ljad']]}, 'NYC': {'acct': ['92742', '34567'], 'c_name': ['hhdls3', ['hquer']]}, 'LONDON': {'acct': ['84720'], 'c_name': ['abc']}}

Categories

Resources