Reputation: 82
So I can't seem to figure out how to effectively implement this. I'm looking to nest a flattened dictionary into dictionary of dictionaries of lists, based on specific keys as inputs. Trying so desperately to learn
Given that my data looks like this:
data= [
{
"player": "Kevin Durant",
"team": "Thunder",
"location": "Oklahoma City",
"points": 15
},
{
"player": "Jeremy Lin",
"team": "Lakers",
"location": "Los Angeles",
"points": 22
},
{
"player": "Kobe Bryant",
"team": "Lakers",
"location": "Los Angeles",
"points": 51
},
{
"player": "Blake Griffin",
"team": "Clippers",
"location": "Los Angeles",
"points": 26
}
]
I'd want to return something like this if I give it parameters like reorder(data,['location','team','player']) for an example
result={
"Los Angeles": {
"Clippers": {
"Blake Griffin": [
{
"points": 26
}
]
},
"Lakers": {
"Kobe Bryant": [
{
"points": 51
}
],
"Jeremy Lin": [
{
"points": 22
}
]
}
},
"Oklahoma City": {
"Thunder": {
"Kevin Durant": [
{
"points": 15
}
]
}
},
}
Upvotes: 1
Views: 61
Reputation: 42133
You can use the setdefault function to automatically build the nesting levels as you go through the data:
data= [
{
"player": "Kevin Durant",
"team": "Thunder",
"location": "Oklahoma City",
"points": 15
},
{
"player": "Jeremy Lin",
"team": "Lakers",
"location": "Los Angeles",
"points": 22
},
{
"player": "Kobe Bryant",
"team": "Lakers",
"location": "Los Angeles",
"points": 51
},
{
"player": "Blake Griffin",
"team": "Clippers",
"location": "Los Angeles",
"points": 26
}
]
nested = dict()
for d in data:
nested.setdefault(d["location"],dict()) \
.setdefault(d["team"], dict()) \
.setdefault(d["player"], list()) \
.append({"points":d["points"]})
output:
print(nested)
{ 'Oklahoma City':
{
'Thunder':
{ 'Kevin Durant': [{'points': 15}] }
},
'Los Angeles':
{
'Lakers':
{
'Jeremy Lin': [{'points': 22}],
'Kobe Bryant': [{'points': 51}]
},
'Clippers':
{ 'Blake Griffin': [{'points': 26}] }
}
}
[EDIT] Generalizing the approach
If you have to do this kind of thing often and on different types of dictionaries or hierarchies, you could generalize it in a function:
def dictNesting(data,*levels):
result = dict()
for d in data:
r = result
for level in levels[:-1]:
r = r.setdefault(d[level],dict())
r = r.setdefault(d[levels[-1]],list())
r.append({k:v for k,v in d.items() if k not in levels})
return result
You would then give the function a list of dictionaries followed by the names of the keys you want to nest:
byLocation = dictNesting(data,"location","team")
{ 'Oklahoma City':
{ 'Thunder': [
{'player': 'Kevin Durant', 'points': 15}]
},
'Los Angeles':
{'Lakers': [
{'player': 'Jeremy Lin', 'points': 22},
{'player': 'Kobe Bryant', 'points': 51}],
'Clippers': [
{'player': 'Blake Griffin', 'points': 26}]
}
}
If you want to group the same data in a different way, you just need to change the order of the field names:
byPlayer = dictNesting(data,"player","location","team")
{ 'Kevin Durant':
{ 'Oklahoma City':
{ 'Thunder': [{'points': 15}] }
},
'Jeremy Lin':
{ 'Los Angeles':
{'Lakers': [{'points': 22}]}
},
'Kobe Bryant':
{ 'Los Angeles':
{'Lakers': [{'points': 51}]}
},
'Blake Griffin':
{ 'Los Angeles':
{'Clippers': [{'points': 26}]}
}
}
From there you can have some fun with the function and improve it to aggregate the data at the lowest nesting level:
def dictNesting(data,*levels,aggregate=False):
result = dict()
for d in data:
r = result
for level in levels[:-1]:
r = r.setdefault(d[level],dict())
r = r.setdefault(d[levels[-1]],[list,dict][aggregate]())
content = ( (k,v) for k,v in d.items() if k not in levels)
if aggregate:
for k,v in content: r.setdefault(k,list()).append(v)
else:
r.append(dict(content))
return result
output:
byCity = dictNesting(data,"location","team",aggregate=True)
{ 'Oklahoma City':
{'Thunder':
{'player': ['Kevin Durant'], 'points': [15]}},
'Los Angeles':
{'Lakers':
{'player': ['Jeremy Lin', 'Kobe Bryant'], 'points': [22, 51]},
'Clippers':
{'player': ['Blake Griffin'], 'points': [26]}
}
}
lakersPlayers = byCity["Los Angeles"]["Lakers"]["player"]
# ['Jeremy Lin', 'Kobe Bryant']
lakersPoints = sum(byCity["Los Angeles"]["Lakers"]["points"])
# 73
Upvotes: 3