Join dictionaries so that previous dictionary names are now 2nd layer keys? - python

I am attempting to organize new dictionaries based on the results of a df.groupby('fruits'). I now have dictionaries setup like the following,
print(type(flavors))
<class 'dict'>
print(flavors)
{'apples':['crisp', 'tart'],
'oranges':['citrusy','juicy', 'sour'],
'bananas':['sweet']}
print(colors)
{'apples':['red', 'green'],
'oranges':['orange','orange', 'rotten'],
'bananas':['yellow']}
print(farms)
{'apples':['upstate orchard', 'dreamy estates'],
'oranges':['crop culture','sandy bay', 'heartland'],
'bananas':['horticulture heros']}
How could I join them in a new dictionary like the following?
print(fruits_dict)
{'apples': {'flavors': ['crisp', 'tart'],
'colors': ['red', 'green'],
'farms': ['upstate orchard', 'dreamy estates'] },
'oranges': {'flavors': ['citrusy','juicy', 'sour'],
'colors': ['orange','orange', 'rotten'],
'farms': ['crop culture','sandy bay', 'heartland'] },
'bananas': {'flavors': ['sweet'],
'colors': ['yellow'],
'farms': ['horticulture heros'] } }

dicts = {'flavors' : flavors, 'colors': colors, 'farms' : farms}
result = {key: { k : dicts[k][key] for k in dicts} for key in flavors}

You can use this example with dict comprehension how to get your output:
flavors = {
"apples": ["crisp", "tart"],
"oranges": ["citrusy", "juicy", "sour"],
"bananas": ["sweet"],
}
colors = {
"apples": ["red", "green"],
"oranges": ["orange", "orange", "rotten"],
"bananas": ["yellow"],
}
farms = {
"apples": ["upstate orchard", "dreamy estates"],
"oranges": ["crop culture", "sandy bay", "heartland"],
"bananas": ["horticulture heros"],
}
out = {
k: {
"flavors": flavors.get(k, []),
"colors": colors.get(k, []),
"farms": farms.get(k, []),
}
for k in (flavors.keys() & colors.keys() & farms.keys())
}
from pprint import pprint
pprint(out)
Prints:
{'apples': {'colors': ['red', 'green'],
'farms': ['upstate orchard', 'dreamy estates'],
'flavors': ['crisp', 'tart']},
'bananas': {'colors': ['yellow'],
'farms': ['horticulture heros'],
'flavors': ['sweet']},
'oranges': {'colors': ['orange', 'orange', 'rotten'],
'farms': ['crop culture', 'sandy bay', 'heartland'],
'flavors': ['citrusy', 'juicy', 'sour']}}

Related

Get keys of dictionary based on rules

given a dictionary
dictionary = {'Animal 1': {'Dog': 'Yes', 'Cat': 'No', 'Color': 'Black'},
'Animal 2': {'Dog': 'Yes', 'Cat': 'No', 'Color': 'Brown'},
'Animal 3': {'Dog': 'No', 'Cat': 'Yes', 'Color': 'Grey'}}
How do I select the Animals that are dogs?
expected output ['Animal 1','Animal 2']
I could use:
pd.DataFrame.from_dict(dictionary).T.loc[pd.DataFrame.from_dict(dictionary).T["Dog"]=='Yes',:].index.to_list()
but it looks very ugly
You can use list comprehension:
dictionary = {
"Animal 1": {"Dog": "Yes", "Cat": "No", "Color": "Black"},
"Animal 2": {"Dog": "Yes", "Cat": "No", "Color": "Brown"},
"Animal 3": {"Dog": "No", "Cat": "Yes", "Color": "Grey"},
}
out = [k for k, d in dictionary.items() if d.get("Dog") == "Yes"]
print(out)
Prints:
['Animal 1', 'Animal 2']
The pandas version could be trimmed by using an intermediate variable so that you don't double calculate the mask. And you don't need .loc for this filter.
df = pd.DataFrame.from_dict(dictionary).T
dogs = df[df["Dog"] == "Yes"].index.to_list()
But this is still complex compared to running through the dict items in another answer to this question. It would only be interesting if there was some future need of the dataframe.

How can I collect key-value pairs of dictionaries into one large dictionary in Python?

I have a dictionary in the following format:
data = {
'Bob': {
'age': 12,
'weight': 150,
'eye_color': 'blue'
},
'Jim': {
'favorite_food': 'cherries',
'sport': 'baseball',
'hobby': 'running'
},
'Tom': {
'strength': 'average',
'endurance': 'high',
'heart_rate': 'low'
}
}
What is the most Pythonic way to concatenate all of the dictionaries within dict into a new dictionary so that I would end up with something like the following:
new_dict = {
'age': 12,
'weight': 150,
'eye_color': 'blue',
'favorite_food': 'cherries',
'sport': 'baseball',
'hobby': 'running',
'strength': 'average',
'endurance': 'high',
'heart_rate': 'low'
}
You can use functools.reduce() to build up the result, unioning one dictionary at a time:
from functools import reduce
data = {
'Bob' : { 'age': 12, 'weight': 150, 'eye_color': 'blue' },
'Jim' : { 'favorite_food': 'cherries', 'sport': 'baseball', 'hobby': 'running' },
'Tom' : { 'strength': 'average', 'endurance': 'high', 'hear_rate': 'low' }
}
result = reduce(lambda x, y: dict(**x, **y), data.values(), {})
print(result)
This outputs:
{'age': 12, 'weight': 150, 'eye_color': 'blue', 'favorite_food': 'cherries',
'sport': 'baseball', 'hobby': 'running', 'strength': 'average',
'endurance': 'high', 'hear_rate': 'low'}
On Python 3.9 or higher, you can use lambda x: x | y, operator.or_, or dict.__or__ instead of lambda x: dict(**x, **y) if you're on Python 3.9 or higher. The latter two are from a suggestion by Mad Physicist.
One option is to use a dictionary comprehension with a nested generator expression:
new_dict = {k: v for d in data.values() for k, v in d.items()}
Another way that's subtly different to to use collections.ChainMap:
new_dict = collections. ChainMap(*data.values())
In this case, new_dict will not be a dict, but will quack like one just fine. Lookup will be a bit slower, but construction will be faster.

Mapping JSON key-value pairs from source to destination using Python

Using Python requests I want to grab a piece of JSON from one source and post it to a destination. The structure of the JSON received and the one required by the destination, however, differs a bit so my question is, how do I best map the items from the source structure onto the destination structure?
To illustrate, imagine we get a list of all purchases made by John and Mary. And now we want to post the individual items purchased linking these to the individuals who purchased them (NOTE: The actual use case involves thousands of entries so I am looking for an approach that would scale accordingly):
Source JSON:
{
'Total Results': 2,
'Results': [
{
'Name': 'John',
'Age': 25,
'Purchases': [
{
'Fruits': {
'Type': 'Apple',
'Quantity': 3,
'Color': 'Red'}
},
{
'Veggie': {
'Type': 'Salad',
'Quantity': 2,
'Color': 'Green'
}
}
]
},
{
'Name': 'Mary',
'Age': 20,
'Purchases': [
{
'Fruits': {
'Type': 'Orange',
'Quantity': 2,
'Color': 'Orange'
}
}
]
}
]
}
Destination JSON:
{
[
{
'Purchase': 'Apple',
'Purchased by': 'John',
'Quantity': 3,
'Type': 'Red',
},
{
'Purchase': 'Salad',
'Purchased by': 'John',
'Quantity': 2,
'Type': 'Green',
},
{
'Purchase': 'Orange',
'Purchased by': 'Mary',
'Quantity': 2,
'Type': 'Orange',
}
]
}
Any help on this would be greatly appreciated! Cheers!
Just consider loop through the dict.
res = []
for result in d['Results']:
value = {}
for purchase in result['Purchases']:
item = list(purchase.values())[0]
value['Purchase'] = item['Type']
value['Purchased by'] = result['Name']
value['Quantity'] = item['Quantity']
value['Type'] = item['Color']
res.append(value)
pprint(res)
[{'Purchase': 'Apple', 'Purchased by': 'John', 'Quantity': 3, 'Type': 'Red'},
{'Purchase': 'Salad', 'Purchased by': 'John', 'Quantity': 2, 'Type': 'Green'},
{'Purchase': 'Orange', 'Purchased by': 'Mary', 'Quantity': 2, 'Type': 'Orange'}]

Add count next to each aggregated value

I currently aggregate a query to get a unique list of strings used in my endpoint. As of now, the fetched data looks like this:
{
"data": [
"Beige",
"Grey",
...
]
}
However, I'm attempting to do something like this:
{
"data": [
{
name: "Beige",
count: 7
},
{
name: "Grey",
count: 3
},
...
]
}
Where count is the amount of times the value occurs in the datbase.
Currently, my viewset is structured like this:
class ProductFiltersByCategory(APIView):
"""
This viewset takes the category parameter from the url and returns related product filters
"""
def get(self, request, *args, **kwargs):
"""
Gets parameter in urls and aggregated filtered result
"""
category = self.kwargs['category']
aggregated_result = Product.objects.filter(
category__parent__name__iexact=category,
status='available'
).distinct().aggregate(data=ArrayAgg('colors__name', distinct=True))
return Response(aggregated_result)
How do i go about making the key-value pair structure i want, as well as do the count for each "color"?
You can annotate the Count of each category and group by that category using .values(). Since your relations involve a many-to-many field, you probably just want to do the query on the Color model rather than the Product model.
Color.objects.filter(
product__category__parent__name__iexact=category,
product__status='available'
).values('name', 'product').annotate(
colors_count=Count('product', distinct=True)
).values_list('name', 'colors_count')
You can do this with collections.Counter:
from random import choices
from collections import Counter
from pprint import pprint
colours = ['White', 'Yellow', 'Blue', 'Red', 'Green', 'Black', 'Brown', 'Azure', 'Ivory', 'Teal', 'Silver', 'Purple', 'Navy blue', 'Pea green', 'Gray',
'Orange', 'Maroon', 'Charcoal', 'Aquamarine', 'Coral', 'Fuchsia', 'Wheat', 'Lime', 'Crimson', 'Khaki', 'Hot pink', 'Magenta', 'Olden', 'Plum', 'Olive', 'Cyan']
data = {"data": choices(colours, k=10)}
pprint(data)
newData = {"data": [{"name": k, "count": v} for k,v in Counter(data["data"]).items()]}
pprint(newData)
Sample Output:
{'data': ['Coral',
'Ivory',
'Red',
'Crimson',
'Azure',
'Ivory',
'Red',
'Red',
'Khaki',
'Hot pink']}
{'data': [{'count': 1, 'name': 'Coral'},
{'count': 2, 'name': 'Ivory'},
{'count': 3, 'name': 'Red'},
{'count': 1, 'name': 'Crimson'},
{'count': 1, 'name': 'Azure'},
{'count': 1, 'name': 'Khaki'},
{'count': 1, 'name': 'Hot pink'}]}
You can use Counter form the collections library to count the values.
You can try something like this:
from collections import Counter
data = get_data_from_endpoint(request)[data]
c = Counter(data)
result = {'data': [{'name': element, 'count': c[element]} for element in c]}

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

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

Categories