String array to nested dictionary Python - 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))

Related

Python: building complex nested lists within a dictionary

I am looking at building lists of lists within a dictionary from an Excel spreadsheet.
My spreadsheet looks like this:
source_item_id
target_item_id
find_sting
replace_sting
source_id1
target_id1
abcd1
efgh1
source_id1
target_id1
ijkl1
mnop1
source_id1
target_id2
abcd2
efgh2
source_id1
target_id2
ijkl2
mnop2
source_id2
target_id3
qrst
uvwx
source_id2
target_id3
yzab
cdef
source_id2
target_id4
ghij
klmn
source_id2
target_id4
opqr
stuv
My output dictionary should looks like this:
{
"source_id1": [{
"target_id1": [{
"find_string": "abcd1",
"replace_string": "efgh1"
},
{
"find_string": "ijkl1",
"replace_string": "mnop1"
}]
},
{
"target_id2": [{
"find_string": "abcd2",
"replace_string": "efgh2"
},
{
"find_string": "ijkl2",
"replace_string": "mnop2"
}]
}],
"source_id2": [{
"target_id3": [{
"find_string": "qrst",
"replace_string": "uvwx"
},
{
"find_string": "yzab",
"replace_string": "cdef"
}]
},
{
"target_id4": [{
"find_string": "ghij",
"replace_string": "klmn"
},
{
"find_string": "opqr",
"replace_string": "stuv"
}]
}]
}
With the following code I only get the last values in each of the lists:
import xlrd
xls_path = r"C:\data\ItemContent.xlsx"
book = xlrd.open_workbook(xls_path)
sheet_find_replace = book.sheet_by_index(1)
find_replace_dict = dict()
for line in range(1, sheet_find_replace.nrows):
source_item_id = sheet_find_replace.cell(line, 0).value
target_item_id = sheet_find_replace.cell(line, 1).value
find_string = sheet_find_replace.cell(line, 2).value
replace_sting = sheet_find_replace.cell(line, 3).value
find_replace_list = [{"find_string": find_string, "replace_sting": replace_sting}]
find_replace_dict[source_item_id] = [target_item_id]
find_replace_dict[source_item_id].append(find_replace_list)
print(find_replace_dict)
--> result
{
"source_id1": ["target_id2", [{
"find_string": "ijkl2",
"replace_sting": "mnop2"
}
]],
"source_id2": ["target_id4", [{
"find_string": "opqr",
"replace_sting": "stuv"
}
]]
}
Your problem is rather complicated by the fact that you have a list of single-key dictionaries as the value of your source ids, but you can follow a pattern of parsing each line for the relevant items and, and then using those to target where you insert appends, or alternatively create new lists:
def process_line(line) -> Tuple[str, str, dict]:
source_item_id = sheet_find_replace.cell(line, 0).value
target_item_id = sheet_find_replace.cell(line, 1).value
find_string = sheet_find_replace.cell(line, 2).value
replace_string = sheet_find_replace.cell(line, 3).value
return source_item_id, target_item_id, {
"find_string": find_string,
"replace_string": replace_string
}
def find_target(target: str, ls: List[dict]) -> int:
# Find the index of the target id in the list
for i in len(ls):
if ls[i].get(target):
return i
return -1 # Or some other marker
import xlrd
xls_path = r"C:\data\ItemContent.xlsx"
book = xlrd.open_workbook(xls_path)
sheet_find_replace = book.sheet_by_index(1)
result_dict = dict()
for line in range(1, sheet_find_replace.nrows):
source, target, replacer = process_line(line)
# You can check here that the above three are correct
source_list = result_dict.get(source, []) # Leverage the default value of the get function
target_idx = find_target(target, source_list)
target_dict = source_list[target_idx] if target_idx >=0 else {}
replace_list = target_dict.get(target, [])
replace_list.append(replacer)
target_dict[target] = replace_list
if target_idx >= 0:
source_list[target_idx] = target_dict
else:
source_list.append(target_dict)
result_dict[source] = source_list
print(result_dict)
I would note that if source_id pointed to a dictionary rather than a list, this could be radically simplified, since we wouldn't need to search through the list for a potentially already-existing list item and then awkwardly replace or append as needed. If you can change this constraint (remember, you can always convert a dictionary to a list downstream), I might consider doing that.

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

Remove duplicate values in different Json Lists python

I know that there are a lot of questions about duplicates but I can't find a solution suitable for me.
I have a json structure like this:
{
"test": [
{
"name2": [
"Tik",
"eev",
"asdv",
"asdfa",
"sadf",
"Nick"
]
},
{
"name2": [
"Tik",
"eev",
"123",
"r45",
"676",
"121"
]
}
]
}
I want to keep the first value and remove all the other duplicates.
Expected Result
{
"test": [
{
"name2": [
"Tik",
"eev",
"asdv",
"asdfa",
"sadf",
"Nick"
]
},
{
"name2": [
"123",
"r45",
"676",
"121"
]
}
]
}
I tried using a tmp to check for duplicates but it didn't seem to work. Also I can't find a way to make it json again.
import json
with open('myjson') as access_json:
read_data = json.load(access_json)
tmp = []
tmp2 = []
def get_synonyms():
ingredients_access = read_data['test']
for x in ingredients_access:
for j in x['name2']:
tmp.append(j)
if j in tmp:
tmp2.append(j)
get_synonyms()
print(len(tmp))
print(len(tmp2))
You can use recursion:
def filter_d(d):
seen = set()
def inner(_d):
if isinstance(_d, dict):
return {a:inner(b) if isinstance(b, (dict, list)) else b for a, b in _d.items()}
_r = []
for i in _d:
if isinstance(i, (dict, list)):
_r.append(inner(i))
elif i not in seen:
_r.append(i)
seen.add(i)
return _r
return inner(d)
import json
print(json.dumps(filter_d(data), indent=4))
Output:
{
"test": [
{
"name2": [
"Tik",
"eev",
"asdv",
"asdfa",
"sadf",
"Nick"
]
},
{
"name2": [
"123",
"r45",
"676",
"121"
]
}
]
}
You are first adding everything to tmp and then to tmp2 because every value was added to tmp before.
I changed the function a little bit to work for your specific test example:
def get_synonyms():
test_list = []
ingredients_access = read_data['test']
used_values =[]
for x in ingredients_access:
inner_tmp = []
for j in x['name2']:
if j not in used_values:
inner_tmp.append(j)
used_values.append(j)
test_list.append({'name2':inner_tmp})
return {'test': test_list}
result = get_synonyms()
print(result)
Output:
{'test': [{'name2': ['Tik', 'eev', 'asdv', 'asdfa', 'sadf', 'Nick']}, {'name2': ['123', 'r45', '676', '121']}]}
Here's a little hackish answer:
d = {'test': [{'name2': ['Tik', 'eev', 'asdv', 'asdfa', 'sadf', 'Nick']},
{'name2': ['Tik', 'eev', '123', 'r45', '676', '121']}]}
s = set()
for l in d['test']:
l['name2'] = [(v, s.add(v))[0] for v in l['name2'] if v not in s]
Output:
{'test': [{'name2': ['Tik', 'eev', 'asdv', 'asdfa', 'sadf', 'Nick']},
{'name2': ['123', 'r45', '676', '121']}]}
This uses a set to track the unique values, and add unique values to set while returning the value back to the list.

Python dictionary to dictionary mapping

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"
}
}

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