One of those days, Can't get me head around this tiny solution. I am looking for generic solution as the length of the key value pairs in parent and child leg could vary.
so data source looks like following
data = {
"New York": [
{ "Name": "John", "QTY": 56 },
{ "Name": "Richard", "QTY": 76 } ],
"Texas": [
{ "Name": "John", "QTY": 34 },
{ "Name": "Stephanie", "QTY": 23 } ],
"Florida": [
{ "Name": "Rebecca", "QTY": 28 },
{ "Name": "Richard", "QTY": 43 } ],
"Arizona": [
{"Name": "Charles", "QTY": 23},
{"Name": "Anne", "QTY": 31},
{"Name": "William", "QTY": 44} ],
"New Mexico": [
{"Name": "Rebecca", "QTY": 56},
{"Name": "Charles", "QTY": 76},
{"Name": "Stephen", "QTY": 21},
{"Name": "William", "QTY": 63} ],
"New Jersey": [
{"Name": "John", "QTY": 56},
{"Name": "Richard", "QTY": 76},
{"Name": "Stephen", "QTY": 45} ] }
Now I want to re-arrange the the dictionary by Name and add values for each state and add None for missing. here is my code
output = {}
for x, y in data.items():
for i in y:
key = i['Name']
try:
output[key].update({x: i['QTY']})
except:
output[key] = {x: i['QTY']}
print(output)
This gives me result as following
{
"John": {
"New York": 56,
"Texas": 34,
"New Jersey": 56
},
"Richard": {
"New York": 76,
"Florida": 43,
"New Jersey": 76
},
"Stephanie": {
"Texas": 23
},
"Rebecca": {
"Florida": 28,
"New Mexico": 56
},
"Charles": {
"Arizona": 23,
"New Mexico": 76
},
"Anne": {
"Arizona": 31
},
"William": {
"Arizona": 44,
"New Mexico": 63
},
"Stephen": {
"New Mexico": 21,
"New Jersey": 45
}
}
What I will like to have is something like following, order is indeed important
{
"John": {
"New York": 56,
"Texas": 34,
"Florida": None,
"Arizona": None,
"New Mexico": None,
"New Jersey": 56
},
.......
.......
"Stephen": {
"New York": None,
"Texas": None,
"Florida": None,
"Arizona": None,
"New Mexico": 21,
"New Jersey": 45
}
}
I was wondering if defautldict from collections can be used. But I am not that savvy with it. Folks, remember I need generic solution as the number of states and number os Names can vary.. Any help is much appreciated.
CodePudding user response:
Here's a straightforward way to do what your question asks:
nameList = []
nameSet = set()
for state, nameQtyList in data.items():
for nameQty in nameQtyList:
name = nameQty["Name"]
if name not in nameSet:
nameSet.add(name)
nameList.append(name)
out = {name:{state:None for state in data} for name in nameList}
for state, nameQtyList in data.items():
for nameQty in nameQtyList:
out[nameQty["Name"]][state] = nameQty["QTY"]
Explanation:
- walk through the input, compiling names in the sequence of first occurrence in
names
- initialize the result
out
to have the desired structure, with an initial value ofNone
for eachQTY
- walk through the input again, updating
out
with eachQTY
encountered in the input.
CodePudding user response:
In your code, instead of:
try:
output[key].update({x: i['QTY']})
except:
output[key] = {x: i['QTY']}
do
try:
output[key].update({x: i['QTY']})
except KeyError:
output[key] = defaultdict(none_generator, {x: i['QTY']})
Here, none_generator
is a function that takes no arguments and returns `None:
def none_generator(): pass
Note: You shouldn't be using a bare
except:
clause anyway. You know youroutput[key].update(...)
line will produce aKeyError
if all is working as expected, so only catch aKeyError
(See What is wrong with using a bare 'except'? for more details)
Now, each value of your output
dictionary is itself a defaultdict
, and getting a non-existent key of these defaultdict
s will return `None:
output = {'John': defaultdict(<function none_generator at 0x12256d6c20>, {'New York': 56, 'Texas': 34, 'New Jersey': 56}), 'Richard': defaultdict(<function none_generator at 0x12256d6c20>, {'New York': 76, 'Florida': 43, 'New Jersey': 76}), 'Stephanie': defaultdict(<function none_generator at 0x12256d6c20>, {'Texas': 23}), 'Rebecca': defaultdict(<function none_generator at 0x12256d6c20>, {'Florida': 28, 'New Mexico': 56}), 'Charles': defaultdict(<function none_generator at 0x12256d6c20>, {'Arizona': 23, 'New Mexico': 76}), 'Anne': defaultdict(<function none_generator at 0x12256d6c20>, {'Arizona': 31}), 'William': defaultdict(<function none_generator at 0x12256d6c20>, {'Arizona': 44, 'New Mexico': 63}), 'Stephen': defaultdict(<function none_generator at 0x12256d6c20>, {'New Mexico': 21, 'New Jersey': 45})}
print(output["John"]["Florida"])
prints None
If you want there to be None
values explicitly at the keys in the dictionary that do not exist, you're going to have to populate the output
dictionary first. Then, remake all dictionaries with their missing values set to None
:
output = {}
for x, y in data.items():
for i in y:
key = i['Name']
all_states.add(x)
try:
output[key].update({x: i['QTY']})
except KeyError:
output[key] = {x: i['QTY']}
for person, person_vals in output.items():
output[person] = {state: person_vals.get(state, None) for state in data}
{'John': {'New York': 56,
'Texas': 34,
'Florida': None,
'Arizona': None,
'New Mexico': None,
'New Jersey': 56},
'Richard': {'New York': 76,
'Texas': None,
'Florida': 43,
'Arizona': None,
'New Mexico': None,
'New Jersey': 76},
'Stephanie': {'New York': None,
'Texas': 23,
'Florida': None,
'Arizona': None,
'New Mexico': None,
'New Jersey': None},
'Rebecca': {'New York': None,
'Texas': None,
'Florida': 28,
'Arizona': None,
'New Mexico': 56,
'New Jersey': None},
'Charles': {'New York': None,
'Texas': None,
'Florida': None,
'Arizona': 23,
'New Mexico': 76,
'New Jersey': None},
'Anne': {'New York': None,
'Texas': None,
'Florida': None,
'Arizona': 31,
'New Mexico': None,
'New Jersey': None},
'William': {'New York': None,
'Texas': None,
'Florida': None,
'Arizona': 44,
'New Mexico': 63,
'New Jersey': None},
'Stephen': {'New York': None,
'Texas': None,
'Florida': None,
'Arizona': None,
'New Mexico': 21,
'New Jersey': 45}}