Update values from a "default dictionary" without removing its keys [duplicate] - python

This question already has answers here:
Update value of a nested dictionary of varying depth
(28 answers)
Closed 4 years ago.
I have two dictionaries, the first is what the default values of the second one should fall back to if they are not present or not defined, and it is somewhat like this:
default_dict = {
'lorem': {
'foo': 'white',
'bar': 'black',
},
'ipsum': {
'xyz': '',
'abc': {},
'qwe': {}
}
}
And the second looks like this:
custom_dict = {
'lorem': {
'bar': 'blue',
},
'ipsum': {
'xyz': 'apple',
'qwe': { 'one': 'strawberry' }
}
}
Is there any way I can "update" from the default_dict with the values from the custom_dict?
The desired result would look something like this:
custom_dict = {
'lorem': {
'foo': 'white',
'bar': 'blue',
},
'ipsum': {
'xyz': 'apple',
'abc': {},
'qwe': { 'one': 'strawberry' }
}
}
I've tried doing default_dict.update(custom_dict) and then custom_dict = default_dict, but as you can imagine I'm just getting custom_dict back untouched... So the keys of default_dict are getting removed when it is updated.

Use:
d={a:b for k,v in custom_dict.items() for a,b in v.items()}
print({k:{a:d.get(a,b) for a,b in v.items()} for k,v in default_dict.items()})
A dictionary comprehension + a nested dictionary comprehension would work.
Output:
{'lorem': {'foo': 'white', 'bar': 'blue'}, 'ipsum': {'xyz': 'apple', 'abc': {}, 'qwe': {'one': 'strawberry'}}}

if the structure of your dictionaries are always like above so the following code works fine:
for item in default_dict:
for value in default_dict[item].keys():
if value not in custom_dict[item].keys():
custom_dict[item].update({value: default_dict[item][value]})
good luck

Related

Would it be considered "pythonic" to use a nested defaultdict where bottom level is defaulted to 0 for counting?

I am building something to sort and add values from an API response. I ended up going with an interesting structure, and I just want to make sure there's nothing inherently wrong with it.
from collections import defaultdict
# Helps create a unique nested default dict object
# for code readability
def dict_counter():
return defaultdict(lambda: 0)
# Creates the nested defaultdict object
ad_data = defaultdict(dict_counter)
# Sorts each instance into its channel, and
# adds the dict values incrimentally
for ad in example:
# Collects channel and metrics
channel = ad['ad_group']['type_']
metrics = dict(
impressions= int(ad['metrics']['impressions']),
clicks = int(ad['metrics']['clicks']),
cost = int(ad['metrics']['cost_micros'])
)
# Adds the variables
ad_data[channel]['impressions'] += metrics['impressions']
ad_data[channel]['clicks'] += metrics['clicks']
ad_data[channel]['cost'] += metrics['cost']
The output is as desired. Again, I just want to make sure I'm not reinventing the wheel or doing something really inefficient here.
defaultdict(<function __main__.dict_counter()>,
{'DISPLAY_STANDARD': defaultdict(<function __main__.dict_counter.<locals>.<lambda>()>,
{'impressions': 14, 'clicks': 4, 'cost': 9}),
'SEARCH_STANDARD': defaultdict(<function __main__.dict_counter.<locals>.<lambda>()>,
{'impressions': 6, 'clicks': 2, 'cost': 4})})
Here's what my input data would look like:
example = [
{
'campaign':
{
'resource_name': 'customers/12345/campaigns/12345',
'status': 'ENABLED',
'name': 'test_campaign_2'
},
'ad_group': {
'resource_name': 'customers/12345/adGroups/12345',
'type_': 'DISPLAY_STANDARD'},
'metrics': {
'clicks': '1', 'cost_micros': '3', 'impressions': '5'
},
'ad_group_ad': {
'resource_name': 'customers/12345/adGroupAds/12345~12345',
'ad': {
'resource_name': 'customers/12345/ads/12345'
}
}
},
{
'campaign':
{
'resource_name': 'customers/12345/campaigns/12345',
'status': 'ENABLED',
'name': 'test_campaign_2'
},
'ad_group': {
'resource_name': 'customers/12345/adGroups/12345',
'type_': 'SEARCH_STANDARD'},
'metrics': {
'clicks': '2', 'cost_micros': '4', 'impressions': '6'
},
'ad_group_ad': {
'resource_name': 'customers/12345/adGroupAds/12345~12345',
'ad': {
'resource_name': 'customers/12345/ads/12345'
}
}
},
{
'campaign':
{
'resource_name': 'customers/12345/campaigns/12345',
'status': 'ENABLED',
'name': 'test_campaign_2'
},
'ad_group': {
'resource_name': 'customers/12345/adGroups/12345',
'type_': 'DISPLAY_STANDARD'},
'metrics': {
'clicks': '3', 'cost_micros': '6', 'impressions': '9'
},
'ad_group_ad': {
'resource_name': 'customers/12345/adGroupAds/12345~12345',
'ad': {
'resource_name': 'customers/12345/ads/12345'
}
}
}
]
Thanks!
There's nothing wrong with the code you have, but the code for copying the values from one dict to another is a bit repetitive and a little vulnerable to mis-pasting a key name. I'd suggest putting the mapping between the keys in a dict so that there's a single source of truth for what keys you're copying from the input metrics dicts and what keys that data will live under in the output:
fields = {
# Map input metrics dicts to per-channel metrics dicts.
'impressions': 'impressions', # same
'clicks': 'clicks', # same
'cost_micros': 'cost', # different
}
Since each dict in your output is going to contain the keys from fields.values(), you have the option of creating these as plain dicts with their values initialized to zero rather than as defaultdicts (this doesn't have any major benefits over defaultdict(int), but it does make pretty-printing a bit easier):
# Create defaultdict of per-channel metrics dicts.
ad_data = defaultdict(lambda: dict.fromkeys(fields.values(), 0))
and then you can do a simple nested iteration to populate ad_data:
# Aggregate input metrics into per-channel metrics.
for ad in example:
channel = ad['ad_group']['type_']
for k, v in ad['metrics'].items():
ad_data[channel][fields[k]] += int(v)
which for your example input produces:
{'DISPLAY_STANDARD': {'impressions': 14, 'clicks': 4, 'cost': 9},
'SEARCH_STANDARD': {'impressions': 6, 'clicks': 2, 'cost': 4}}
I think you overthought this one a bit. Consider this simple function that sums two dicts:
def add_dicts(a, b):
return {
k: int(a.get(k, 0)) + int(b.get(k, 0))
for k in a | b
}
Using this func, the main loop gets trivial:
stats = {}
for obj in example:
t = obj['ad_group']['type_']
stats[t] = add_dicts(stats.get(t, {}), obj['metrics'])
That's it. No defaultdicts needed.

List of Dictionary - How to combine a list of dictionary Python

a =[{
"id":"1",
"Name":'BK',
"Age":'56'
},
{
"id":"1",
"Sex":'Male'
},
{
"id":"2",
"Name":"AK",
"Age":"32"
}]
I have a list of dictionary with a person information split in multiple dictionary as above for ex above id 1's information is contained in first 2 dictionary , how can i get an output of below
{1: {'Name':'BK','Age':'56','Sex':'Male'}, 2: { 'Name': 'AK','Age':'32'}}
You can use a defaultdict to collect the results.
from collections import defaultdict
a =[{ "id":"1", "Name":'BK', "Age":'56' }, { "id":"1", "Sex":'Male' }, { "id":"2", "Name":"AK", "Age":"32" }]
results = defaultdict(dict)
key = lambda d: d['id']
for a_dict in a:
results[a_dict.pop('id')].update(a_dict)
This gives you:
>>> results
defaultdict(<class 'dict'>, {'1': {'Name': 'BK', 'Age': '56', 'Sex': 'Male'}, '2': {'Name': 'AK', 'Age': '32'}})
The defaultdict type behaves like a normal dict, except that when you reference an unknown value, a default value is returned. This means that as the dicts in a are iterated over, the values (except for id) are updated onto either an existing dict, or an automatic newly created one.
How does collections.defaultdict work?
Using defaultdict
from collections import defaultdict
a = [{
"id": "1",
"Name": 'BK',
"Age": '56'
},
{
"id": "1",
"Sex": 'Male'
},
{
"id": "2",
"Name": "AK",
"Age": "32"
}
]
final_ = defaultdict(dict)
for row in a:
final_[row.pop('id')].update(row)
print(final_)
defaultdict(<class 'dict'>, {'1': {'Name': 'BK', 'Age': '56', 'Sex': 'Male'}, '2': {'Name': 'AK', 'Age': '32'}})
You can combine 2 dictionaries by using the .update() function
dict_a = { "id":"1", "Name":'BK', "Age":'56' }
dict_b = { "id":"1", "Sex":'Male' }
dict_a.update(dict_b) # {'Age': '56', 'Name': 'BK', 'Sex': 'Male', 'id': '1'}
Since the output the you want is in dictionary form
combined_dict = {}
for item in a:
id = item.pop("id") # pop() remove the id key from item and return the value
if id in combined_dict:
combined_dict[id].update(item)
else:
combined_dict[id] = item
print(combined_dict) # {'1': {'Name': 'BK', 'Age': '56', 'Sex': 'Male'}, '2': {'Name': 'AK', 'Age': '32'}}
from collections import defaultdict
result = defaultdict(dict)
a =[{ "id":"1", "Name":'BK', "Age":'56' }, { "id":"1", "Sex":'Male' }, { "id":"2", "Name":"AK", "Age":"32" }]
for b in a:
result[b['id']].update(b)
print(result)
d = {}
for p in a:
id = p["id"]
if id not in d.keys():
d[id] = p
else:
d[id] = {**d[id], **p}
d is the result dictionary you want.
In the for loop, if you encounter an id for the first time, you just store the incomplete value.
If the id is in the existing keys, update it.
The combination happens in {**d[id], **p}
where ** is unpacking the dict.
It unpacks the existing incomplete dict associated withe the id and the current dict, then combine them into a new dict.

creating a list of dictionary in python

from the given input
lists = ["7ee57f24", "deadbeef"]
I want to get the following output
l1': [
{
'd':
{
'id': '7ee57f24'
}
},
{
'd':
{
'id': 'deadbeed'
}
}
]
I have tried this code
lists = ["7ee57f24", "deadbeef"]
l1 = {"d":[{"id": lis} for lis in lists]}
print(l1)
but it gives me wrong output
{'d': [{'id': '7ee57f24'}, {'id': 'deadbeef'}]}
Use the following:
lists = ["7ee57f24", "deadbeef"]
l1 = [
{"d": {"id": id_}}
for id_ in lists
]
print(l1)
Output:
[{'d': {'id': '7ee57f24'}}, {'d': {'id': 'deadbeef'}}]

How do I merge lists of dicts based on dict keys? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Given two lists of dicts:
a = [
{ 'some': 'data',
'foo': 'bar',
'hash': '0x123456'
},
{ 'some': 'otherdata',
'foo': 'foobar',
'hash': '0x7890123'
}
]
b = [
{ 'some': 'thing',
'foo': 'oof',
'hash': '0x123456'
},
{ 'some': 'one',
'foo': 'raboof',
'hash': '0x7890123'
}
]
How can I "merge" them based on the value of hash? I'd like dicts with the same hash value put together in a list:
c = [
[
{ 'some': 'data',
'foo': 'bar',
'hash': '0x123456'
},
{ 'some': 'thing',
'foo': 'oof',
'hash': '0x123456'
},
],
[
{ 'some': 'otherdata',
'foo': 'foobar',
'hash': '0x7890123'
},
{ 'some': 'one',
'foo': 'raboof',
'hash': '0x7890123'
}
]
]
Is this possible?
If you want to group all data by hash, it's easier to use dictionary to do this in python.
For example, using the two list you provided, you can write code like this:
from collections import defaultdict
# initiate a defaultdict as key-value container for items that has the same hash
container = defaultdict(list)
# adding items to the container
for item in a + b:
hash = item['hash']
container[hash].append(item)
# then converting the dictionary items to a list
c = list(container.values())

How to set a new keys from existing key:value pair of a defaultdict?

I have a defaultdict(dict) values as:
defaultdict(<class 'dict'>, {
'AL2G22360.t1_Sp': {
'locus': 'AL2G22360.t1',
'length': '663',
'strain': 'Sp'},
'AL2G22360.t1_My': {
'locus': 'AL2G22360.t1',
'length': '389',
'strain': 'My'},
'AL2G22220.t1_My': {
'locus': 'AL2G22220.t1',
'length': '865',
'strain': 'My'},
'AL2G22220.t1_My': {
'locus': 'AL2G22220.t1',
'length': '553',
'strain': 'My' ........}})
Say, I want to change it in a way that the major key is value of variable **locus**. Since, there is a duplicate for locus value (non unique, but some may be unique), I want to have another sub keys My vs. Sp from variable **strain**. The rest of the data can be as it is.
Expected output:
defaultdict(<class 'dict'>, {
'AL2G22360.t1': {
'Sp': {
'keys': 'AL2G22360.t1_Sp',
'length': '663'},
'My': {
'keys': 'AL2G22360.t1_My',
'length': '389'}},
'AL2G22220.t1': {
'My': {
'keys': 'AL2G22220.t1_My',
'length': '865'},
'Sp': {
'keys': 'AL2G22220.t1_Sp',
'length': '553'}, .....}})
I'd do it like this:
result = defaultdict(lambda: defaultdict(dict))
for k, v in a.items():
result[v['locus']][v['strain']] = { 'keys': k, 'length': v['length'] }
return result
This creates a defaultdict whose values again are defaultdicts whose values are dicts. (This matches your specified output.) Then this gets filled in a straight forward manner by iterating through the original and copying all values into the new format.

Categories