Flatten nested dictionary - python

Input:
my_list = [
{'Name':'Jack', 'Surname': 'Reacher', 'Blogs':{'BlogName': 'python2', 'Date':'20200101' }},
{'Name':'Jack2', 'Surname': 'Reacher2', 'Blogs':{'BlogName':'Python3','Date':'20200201'}}
]
Expected Output:
arr_1 = [ {'Name':'Jack', 'Surname':'Reacher'}, {'Name':'Jack2', 'Surname':'Reacher2'} ]
arr_2 = [ {'ParentId':'Jack', 'BlogName':'python2','Date1':'20200101'},
{'ParentId':'Jack2','BlogName':'Python3','Date1':'20200201'} ]
Output I get:
arr_1 = [ {'Name': 'Jack'}, {'Surname': 'Reacher'}, {'Name': 'Jack2'}, {'Surname': 'Reacher2'} ]
arr_2 = [{'ParentId': 'Jack'}, {'BlogName': 'python2', 'Date': '20200101'},
{'ParentId': 'Jack2'}, {'BlogName': 'Python3', 'Date': '20200201'}]
How should I flatten the dictionary for the 'arr_2' ?
for lis in my_list:
for k, v in lis.items():
if not k == 'Blogs':
arr_1.append({k:v})
for lis in my_list:
for k, v in lis.items():
if k == 'Surname':
continue
elif k == 'Name':
arr_2.append({'ParentId':v})
else:
arr_2.append(v)
print(arr_1)
print(arr_2)
Also, is there a pythonic way to do this ? I feel, I'm over doing it.

Notice how arr2 is processed before arr1. We remove the 'Blogs' inner dict when processing arr2 using pop. Also the ** operator is useful as it flattens a dict.
arr2 = [{'ParentId':d.get('Name'),**d.pop('Blogs')} for d in my_list]
arr1 = my_list[:]
#Output
[{'Name': 'Jack', 'Surname': 'Reacher'}, {'Name': 'Jack2', 'Surname': 'Reacher2'}]
[{'ParentId': 'Jack', 'BlogName': 'python2', 'Date': '20200101'}, {'ParentId': 'Jack2', 'BlogName': 'Python3', 'Date': '20200201'}]

Related

Create a new list by summing up existing list in Python

I have a list like this in Python:
my_list = [
{"id":1,"symbol":"ABC","quantity":100},
{"id":2,"symbol":"PQR","quantity":500},
{"id":3,"symbol":"ABC","quantity":300},
{"id":1,"symbol":"XYZ","quantity":320},
{"id":3,"symbol":"PQR","quantity":800},
]
I wanted to sum up a quantity which have similar symbols and create a new list. The new_list will look like this:
new_list = [
{ "symbol":"ABC","total_quantity":400,"ids":"1 3"}
{ "symbol":"PQR","total_quantity":1300,"ids":"2 3"}
{ "symbol":"XYZ","total_quantity":320,"ids":"1"}
]
I am fairly new to Python and tried some ways but not working. What can I try to achieve this?
A nice solution, is to use a defaultdict that facilitates the grouping per symbol, you just need a final step to build the ids
from collections import defaultdict
my_list = [{"id": 1, "symbol": "ABC", "quantity": 100},
{"id": 2, "symbol": "PQR", "quantity": 500},
{"id": 3, "symbol": "ABC", "quantity": 300},
{"id": 1, "symbol": "XYZ", "quantity": 320},
{"id": 3, "symbol": "PQR", "quantity": 800}, ]
expected = defaultdict(lambda: {'symbol': '', 'ids': [], "total_quantity": 0})
for row in my_list:
expected[row['symbol']]['symbol'] = row['symbol']
expected[row['symbol']]['ids'].append(str(row['id']))
expected[row['symbol']]['total_quantity'] += row['quantity']
result = [{**x, 'ids': " ".join(x['ids'])} for x in expected.values()]
if you are fine to use pandas, you can take this approach
import pandas as pd
df = pd.DataFrame.from_dict(my_list)
df1 = df.groupby('symbol').agg({'quantity':'sum',
'id':lambda x: ' '.join(map(str, x))}).reset_index()
Out:
{'0': {'symbol': 'ABC', 'quantity': 400, 'id': '1 3'},
'1': {'symbol': 'PQR', 'quantity': 1300, 'id': '2 3'},
'2': {'symbol': 'XYZ', 'quantity': 320, 'id': '1'}}
Something like this should work for you:
my_list = [
{"id":1,"symbol":"ABC","quantity":100},
{"id":2,"symbol":"PQR","quantity":500},
{"id":3,"symbol":"ABC","quantity":300},
{"id":1,"symbol":"XYZ","quantity":320},
{"id":3,"symbol":"PQR","quantity":800},
]
new_list = []
for item in my_list:
if item["symbol"] not in [new_item["symbol"] for new_item in new_list]:
new_list.append({"symbol":item["symbol"],"total_quantity":item["quantity"],"ids":str(item["id"])})
else:
for new_item in new_list:
if item["symbol"] == new_item["symbol"]:
new_item["total_quantity"] += item["quantity"]
new_item["ids"] += " "+str(item["id"])
break
print(new_list)
If you prefer a core Python solution, you can do:
my_list = [
{"id":1,"symbol":"ABC","quantity":100},
{"id":2,"symbol":"PQR","quantity":500},
{"id":3,"symbol":"ABC","quantity":300},
{"id":1,"symbol":"XYZ","quantity":320},
{"id":3,"symbol":"PQR","quantity":800},
]
res = []
for sym in sorted(set([x['symbol'] for x in my_list])):
tmp = {}
tmp['symbol'] = sym
tmp['ids'] = r' '.join([str(x['id']) for x in my_list if x['symbol'] == sym])
tmp['total_quantity'] = sum([x['quantity'] for x in my_list if x['symbol'] == sym])
res.append(tmp)
print(res)
#[{'symbol': 'ABC', 'total_quantity': 400, 'ids': '1 3'},
# {'symbol': 'PQR', 'total_quantity': 1300, 'ids': '2 3'},
# {'symbol': 'XYZ', 'total_quantity': 320, 'ids': '1'}]

Update a list of Dict based on another Dict

I have a dict as follows.
dict = {'P': ['Demo'], 'Q': ['PMS']}
And I have a list of Dict as follows.
all = [{'Name': 'PMS'}, {'Name': 'Demo'}]
I need to have the dict's respective value in all as bellow.
new_list = [{'Name': 'PMS','Code': 'Q'}, {'Name': 'Demo','Code': 'P'}]
In this specific case, given that the values are just strings and therefore hashable objects, you can use a dictionary of reverse mappings. Be aware that it could not be used if the values were not hashable.
dct = {'P': ['Demo'], 'Q': ['PMS']}
all = [{'Name': 'PMS'}, {'Name': 'Demo'}]
reverse_mapping = {v[0]:k for k, v in dct.items()}
new_list = [d.copy() for d in all]
for d in new_list:
d['Code'] = reverse_mapping[d['Name']]
print(new_list)
This gives:
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
dct = {'P': ['Demo'], 'Q': ['PMS']}
all_ = [{'Name': 'PMS'}, {'Name': 'Demo'}]
out = [dict(**l, Code=next(k for k, v in dct.items() if l['Name'] in v)) for l in all_]
print(out)
Prints:
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
Or: you can make temporary dictionary:
tmp = {v[0]:k for k, v in dct.items()}
out = [dict(**l, Code=tmp[l['Name']]) for l in all_]
print(out)
You could make an inverted dictionary of codes, then go through the list of dictionaries and add the codes in:
codes = {"P": ["Demo"], "Q": ["PMS"]}
lst = [{"Name": "PMS"}, {"Name": "Demo"}]
inverted_codes = {value: key for key, values in codes.items() for value in values}
# {'Demo': 'P', 'PMS': 'Q'}
for dic in lst:
code = dic["Name"]
dic["Code"] = inverted_codes[code]
print(lst)
Output
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]

how to create a new key from a value of another key Python

I have a list of dictionaries that looks like the following:
data = [{'Name': 'Paul', 'Date': '20200412', 'ID': '1020'}, {'Name': 'Frank', 'Date': '20200413', 'ID': '1030'}, {'Name': 'Anna', 'Date': '20200414', 'ID': '1040'}]
I need to create a new list of dictionaries, where ID's value would be the key, and the value is another dictionary with key/values associated with this specific ID.
This is the desired output:
new_data = [{'1020': {'Name': 'Paul', 'Date': '20200412'}},
{'1030': {'Name': 'Frank', 'Date': '20200413'}},
{'1040': {'Name': 'Anna', 'Date': '20200414'}}]
I have tried:
for index, my_dict in enumerate(data):
new_data = []
key = my_dict['ID']
new_data.append(key)
But that only assigned the key value, not sure how to push it into into a new dict along with other key/values.
>>> [{i['ID']: {k:v for k,v in i.items() if k != 'ID'}} for i in data]
[{'1020': {'Name': 'Paul', 'Date': '20200412'}},
{'1030': {'Name': 'Frank', 'Date': '20200413'}},
{'1040': {'Name': 'Anna', 'Date': '20200414'}}]
new_data = []
for index, my_dict in enumerate(data):
key = my_dict['ID']
del my_dict['ID']
new_data.append({key:data[index]})
To add the other values you simply need to create a new dict like this:
new_data.append( key:{
'name':my_dict['name']
'Date':my_dict['date']
}
You also don't need to make the 'key' variable, you can just use 'my_dict['ID']'
You could try this list comprehension:
[{x["ID"]: {k: v for k, v in x.items() if k != "ID"}} for x in data]
Which assigns ID as the parent key to the dictionary, and filters out the ID key from the child dictionary inside a dict comprehension
Which could be broken down into this:
result = []
for x in data:
result.append({x["ID"]: {k: v for k, v in x.items() if k != "ID"}})
And even to a straightforward loop approach:
result = []
for x in data:
dic = {x["ID"]: {}}
for k, v in x.items():
if k != "ID":
dic[x["ID"]][k] = v
result.append(dic)
Output:
[{'1020': {'Name': 'Paul', 'Date': '20200412'}}, {'1030': {'Name': 'Frank', 'Date': '20200413'}}, {'1040': {'Name': 'Anna', 'Date': '20200414'}}]

Python - Counting and adding items to a new dictionary

I have a list with quite a few entries that looks like this:
first_list = [
{'manager': 'manager1', 'template_id': '12345', 'template_title': 'Template Title1'},
{'manager': 'manager2', 'template_id': '12346', 'template_title': 'Template Title2'},
{'manager': 'manager23', 'template_id': '12345', 'template_title': 'Template Title1'}]
I want to count how many times a template_id is in the list, then add those to a new list of dictionaries (or if there is a better way to do this, I'm all ears).
So end result would be:
second_list = [
{'template_id': '12345', , 'template_title': 'Template Title1', 'count': '5'},
{'template_id': '12346', , 'template_title': 'Template Title2', 'count': '3'},
{'template_id': '12347', , 'template_title': 'Template Title3', 'count': '4''}]
I'm not sure how to iterate through this properly to get the count of the template_id. Any help is greatly appreciate. Thank you!
You can sort the list by template_id and the group items by template_id using itertools.groupby, like this:
import itertools
import operator
first_list = [
{"manager": "manager1", "template_id": "12345", "template_title": "Template Title1"},
{"manager": "manager2", "template_id": "12346", "template_title": "Template Title2"},
{"manager": "manager23", "template_id": "12345", "template_title": "Template Title1"},
]
get_template_id = operator.itemgetter("template_id")
first_list.sort(key=get_template_id)
second_list = []
for template_id, group in itertools.groupby(first_list, key=get_template_id):
group = list(group)
first = group[0]
entry = {
"template_id": template_id,
"template_title": first["template_title"],
"count": len(group),
}
second_list.append(entry)
print(second_list)
You get:
[
{'template_id': '12345', 'template_title': 'Template Title1', 'count': 2},
{'template_id': '12346', 'template_title': 'Template Title2', 'count': 1},
]
A possible solution, without utilizing external libraries:
counts = {}
for d in first_list:
key = (d['template_id'], d['template_title'])
counts[key] = counts.get(key, 0) + 1
second_list = [{'template_id': k[0], 'template_title': k[1], 'counts': v} for k, v in counts.items()]
So we can use itertools.groupby to achieve the desired result. The caveat for itertools.groupby is that your data must be pre-sorted on the grouped keys.
TO_KEEP = ['template_id', 'template_title']
def sort_key(item):
return [item.get(k) for k in TO_KEEP]
results = []
sorted_data = sorted(first_list, key=sort_key)
for k, v in itertools.groupby(sorted_data, key=sort_key):
temp = dict(zip(TO_KEEP, k))
temp.update({'count': len(list(v))})
results.append(temp)
print(results)
[{'template_id': '12345', 'template_title': 'Template Title1', 'count': 2},
{'template_id': '12346', 'template_title': 'Template Title2', 'count': 1}]
You can do this ny first creating a dictionary of dictionaries:
dict_1 = {}
for sub_dict in first_list:
template_id = sub_dict['template_id']
template_title = sub_dict['template_title']
if template_id not in dict_1:
dict_1[template_id] = {'template_id' : template_id, 'template_title': template_title, 'count' : 0}
dict_1[template_id]['count'] += 1
Now all you need to do is convert this to a list:
second_list = list(dict_1.values())
you could use Counter from collections module with list comprehension:
from collections import Counter
count = Counter([e['template_id'] for e in first_list])
id_title = {e['template_id']: e['template_title'] for e in first_list}
second_list = [{'template_id': i, 'template_title': id_title[i], 'count': c} for i, c in count.items()]
output:
[{'template_id': '12345', 'template_title': 'Template Title1', 'count': 2},
{'template_id': '12346', 'template_title': 'Template Title2', 'count': 1}]

How to split string values across a list of dictionaries, so that each part of the split string is in it's own dictionary?

How to split string values across a list of dictionaries, so that each part of the split string is in it's own dictionary?
For example:
lst = [{item:'321,121,343','name':'abby','amount':'400'},{item:'111,222,333','name':'chris','amount':'500'}]
I'd like each part of the item key-value pair to be in its own dictionary
Desired_List = [{item:'321','name':'abby','amount':'400'},{item:'121','name':'abby','amount':'400'},{item:'343','name':'abby','amount':'400'},{item:'111','name':'chris','amount':'500'},{item:'222','name':'chris','amount':'500'},{item:'333','name':'chris','amount':'500'}]
I've tried this with no luck:
[li.update({li['item']:spl}) for li in lst for spl in li['item'].split(',')]
return Li
def unpack_dict(d, field, unpack):
packed = d.pop(field)
for item in unpack(packed):
result = d.copy()
result[field] = item
yield result
lst = [
{'item':'321,121,343','name':'abby','amount':'400'},
{'item':'111,222,333','name':'chris','amount':'500'}
]
new_lst = [
ud
for d in lst
for ud in unpack_dict(d, "item", lambda item: item.split(","))
]
gives new_lst =
[
{'amount': '400', 'item': '321', 'name': 'abby'},
{'amount': '400', 'item': '121', 'name': 'abby'},
{'amount': '400', 'item': '343', 'name': 'abby'},
{'amount': '500', 'item': '111', 'name': 'chris'},
{'amount': '500', 'item': '222', 'name': 'chris'},
{'amount': '500', 'item': '333', 'name': 'chris'}
]
Here is a working script:
lst = [{'item':'321,121,343','name':'abby','amount':'400'}, {'item':'321,121,343','name':'chris','amount':'500'}]
new_list = [{'item':j, 'name': i['name'], 'amount': i['amount']} for i in lst for j in i['item'].split(',')]
>>> print new_list
[{item:'321','name':'abby','amount':'400'},{item:'121','name':'abby','amount':'400'},{item:'343','name':'abby','amount':'400'},{item:'111','name':'chris','amount':'500'},{item:'222','name':'chris','amount':'500'},{item:'333','name':'chris','amount':'500'}]
Demo: http://repl.it/RaE

Categories

Resources