Python: How to store multiple values for one dictionary key - python

I want to store a list of ingredients in a dictionary, using the ingredient name as the key and storing both the ingredient quantity and measurement as values. For example, I would like to be able to do something like this:
ingredientList = {'flour' 500 grams, 'tomato' 10, 'mozzarella' 250 grams}
With the 'tomato' key, tomatoes do not have a measurement, only a quantity. Would I be able to achieve this in Python? Is there an alternate or more efficient way of going about this?

If you want lists just use lists:
ingredientList = {'flour': [500,"grams"], 'tomato':[10], 'mozzarella' :[250, "grams"]}
To get the items:
weight ,meas = ingredientList['flour']
print(weight,meas)
(500, 'grams')
If you want to update just ingredientList[key].append(item)

You could use another dict.
ingredientList = {
'flour': {'quantity': 500, 'measurement': 'grams'},
'tomato': {'quantity': 10},
'mozzarella': {'quantity': 250, 'measurement': 'grams'}
}
Then you could access them like this:
print ingredientList['mozzarella']['quantity']
>>> 250

Related

Django annotate field value from external dictionary

Lets say I have a following dict:
schools_dict = {
'1': {'points': 10},
'2': {'points': 14},
'3': {'points': 5},
}
And how can I put these values into my queryset using annotate?
I would like to do smth like this, but its not working
schools = SchoolsExam.objects.all()
queryset = schools.annotate(
total_point = schools_dict[F('school__school_id')]['points']
)
Models:
class SchoolsExam(Model):
school = ForeignKey('School', on_delete=models.CASCADE),
class School(Model):
school_id = CharField(),
This code gives me an error KeyError: F(school__school_id)
You can not work with F objects in a lookup, since a dictionary does not "understand" F-objects.
You can translate this to a conditional expression [Django-doc]:
from django.db.models import Case, Value, When
schools = SchoolsExam.objects.annotate(
total_point=Case(
*[
When(school__school_id=school_id, then=Value(v['points']))
for school_id, v in school_dict.items()
]
)
)
This will thus "unwind" the dictionary into CASE WHEN school_id=1 THEN 10 WHEN school_id=2 THEN 14 WHEN school_id=3 THEN 5.
However using data in a dictionary often does not make much sense: usually you store this in a table and perform a JOIN.

multiply keys of dictionary to each other

the dictionary I have is:
teachers = [
{'Name':'Mahdi Valikhani', 'phoneN':'+989012345679', 'hours':6, 'payment':50000, 'salaries':[2]*[3]},
{'Name':'Ali Afaghi', 'phoneN':'+989011234567', 'hours':8, 'payment':45000},
{'Name':'Hossein Alizadeh', 'phoneN':'+989011234867', 'hours':8, 'payment':45000},
]
and I want to somehow multiply hours to payment to have the salary!
I have tried multiplying but it gives me an error and the error says you can not multiply strings into integers!
help please!
First of all remove 'salaries':[2]*[3] from the first dict, and then run
If you want to update the existing dictionaries.
for t in teachers:
t["salary"] = t["hours"] * t["payment"]
Note: make sure hours and payment should be numeric, if you are not sure, then you can convert it
for t in teachers:
try:
t["salary"] = int(t["hours"]) * float(t["payment"])
except ValueError:
pass # fallback case
Iterate over each dict and compute salaries and add new key and value like below:
teachers = [
{'Name':'Mahdi Valikhani', 'phoneN':'+989012345679', 'hours':6, 'payment':50000},
{'Name':'Ali Afaghi', 'phoneN':'+989011234567', 'hours':8, 'payment':45000},
{'Name':'Hossein Alizadeh', 'phoneN':'+989011234867', 'hours':8, 'payment':45000},
]
for dct in teachers:
dct['salaries'] = dct['hours']*dct['payment']
print(teachers)
Output:
[{'Name': 'Mahdi Valikhani', 'phoneN': '+989012345679', 'hours': 6, 'payment': 50000, 'salaries': 300000},
{'Name': 'Ali Afaghi', 'phoneN': '+989011234567', 'hours': 8, 'payment': 45000, 'salaries': 360000},
{'Name': 'Hossein Alizadeh', 'phoneN': '+989011234867', 'hours': 8, 'payment': 45000, 'salaries': 360000}]

making old keys the values for a new dictionary with list comprehension

I am trying to make a new list of dictionaries using list comprehension. I have an old list that has 'age' and 'email' keys, with their associated values. I am wanting to create a new list of dictionaries, where 'age' and 'email' are the VALUES of new keys called 'new_age', and 'new_email'.
How would I accomplish this?
entries = [{'age': 65, 'name': 'Tim', 'email': 'tim#bob.com'},{'age': 72, 'name': 'Andy', 'email': 'andy#bob.com'},{'age': 50, 'name': 'Bob', 'email': 'bob#bob.com'}, {'age': 30, 'name': 'Shelly', 'email': 'shelly#shelly.com'}]
x =[{dictionary['new_age'],dictionary['new_email']} for dictionary in entries if dictionary['age'] >= 50]
so my new list of dictionaries 'x' is supposed to make a new list of dictionaries if 'age' >= 50, and then I want just the 'age' and 'email' of that entry in a new dictionary.
so the form will look like this
[{'new_age': 65, 'new_email': bob#bob.com}, {}, etc]
my x list is just an example and it prints out the email, and age if age is above 50 but I need the key pairs in there as well, and this is where I am stuck.
{dictionary['new_age'],dictionary['new_email']} creates a set, not a dictionary (and it wouldn't work anyway because dictionary, which is an element of entries, doesn't contain the keys new_age and new_email)
To create a dictionary, you need key-value pairs like so:
[
{'new_email': dictionary['email'], 'new_age': dictionary['age']}
for dictionary in entries if dictionary['age'] >= 50
]
which gives what you're looking for:
[{'new_email': 'tim#bob.com', 'new_age': 65},
{'new_email': 'andy#bob.com', 'new_age': 72},
{'new_email': 'bob#bob.com', 'new_age': 50}]
You just need to fix how you're doing the dictionary comprehension
x = [
{'new_age': dictionary['age'], 'new_email': dictionary['email']}
for dictionary in entries if dictionary['age'] >= 50
]

Matching from dictionaries based on sum of multiple values

I have two dictionaries as follows. One for services and the other for suppliers who can do the service. Each service can be provided by multiple suppliers.
service = {'service1': {'serviceId': 's0001', 'cost': 220},
'service2': {'serviceId': 's0002', 'cost': 130}....}
supplier = {'supplier1': {'supplierId': 'sup1', 'bid': 30},
'supplier2': {'supplierId': 'sup2', 'bid': 12},
'supplier3': {'supplierId': 'sup3', 'bid': 30}....}
I want to have a new dictionary of matching services to suppliers based on the sum of multiple bids is greater than or equal the cost of service. Something Like:
matched = {'service1': [sup1, sup2, sup100],
'service2': [sup20, sup64, sup200, sup224]....}
Assuming we have huge number of entries in both dictionaries, what is a good way for such required matching? no restrictions on the number of suppliers that can provide a single service.
I tired the following but did not work.
match = {}
for key, value in service.items():
if service[key]['cost'] >= supplier[key]['bid']:
match[key] = [sup for sup in supplier[key]['supplierID']]
Here is the expected output:
matched = {'service1': [sup1, sup2, sup100], 'service2': [sup20, sup64, sup200, sup224]....}
I assume that we have huge number of entries in both dictionaries. This is how I would approach the problem:
import numpy as np
# data
service = {'service1': {'serviceId': 's0001', 'cost': 12},
'service2': {'serviceId': 's0002', 'cost': 30}}
supplier = {'supplier1': {'supplierId': 'sup1', 'bid': 30},
'supplier2': {'supplierId': 'sup2', 'bid': 12},
'supplier3': {'supplierId': 'sup3', 'bid': 30}}
# lists of suppliers' IDs and bids
sups, bids = list(), list()
for key, info in supplier.items():
sups.append(info['supplierId'])
bids.append(info['bid'])
# sorted lists of suppliers' IDs and bids to allow binary search
bids, sups = zip(*sorted(zip(bids, sups)))
# main loop
matched = dict()
for key, info in service.items():
matched[key] = sups[:np.searchsorted(bids, info['cost'], side='right')]
matched:
{'service1': ('sup2',), 'service2': ('sup2', 'sup1', 'sup3')}
This code does not implement easy handling of new entries but allows to do so. For every new service_record we have to perform a single binary search, for every new supplier_record we have to perform a single binary search and a loop over service to update matched.
The code may be and should be improved depending on the specific requirements and the way you use to store the data.

Django complex query based on dicts

Tldr of Problem
Frontend is a form that requires a complex lookup with ranges and stuff across several models, given in a dict. Best way to do it?
Explanation
From the view, I receive a dict of the following form (After being processed by something else):
{'h_index': {"min": 10,"max":20},
'rank' : "supreme_overlord",
'total_citations': {"min": 10,"max":400},
'year_began': {"min": 2000},
'year_end': {"max": 3000},
}
The keys are column names from different models (Right now, 2 separate models, Researcher and ResearchMetrics), and the values are the range / exact value that I want to query.
Example (Above)
Belonging to model Researcher :
rank
year_began
year_end
Belonging to model ResearchMetrics
total_citations
h_index
Researcher has a One to Many relationship with ResearchMetrics
Researcher has a Many to Many relationship with Journals (not mentioned in question)
Ideally: I want to show the researchers who fulfill all the criteria above in a list of list format.
Researcher ID, name, rank, year_began, year_end, total_citations, h_index
[[123, "Thomas", "professor", 2000, 2012, 15, 20],
[ 343 ... ]]
What's the best way to go about solving this problem? (Including changes to form, etc?) I'm not very familiar with the whole form query model thing.
Thank you for your help!
To dynamically perform a query you pass a dict with items 'fieldname__lookuptype': value as **kwargs to Model.objects.filter.
So to filter for rank, year_began and year_end in your example above, you would do this:
How exactly you do the transformation depends on how variable this incoming dictionary is. An example could be something like this:
filter_in = {
'h_index': {"min": 10,"max":20},
'rank' : "supreme_overlord",
'total_citations': {"min": 10,"max":400},
'year_began': {"min": 2000},
'year_end': {"max": 3000},
}
LOOKUP_MAPPING = {
'min': 'gt',
'max': 'lt'
}
filter_kwargs = {}
for field in RESEARCHER_FIELDS:
if not field in filter_in:
continue
filter = filter_in[field]
if isinstance(filter, dict):
for filter_type, value in filter.items():
lookup_type = LOOKUP_MAPPING[filter_type]
lookup = '%s__%s' % (field, lookup_type)
filter_dict[lookup] = value
else:
filter_dict[field] = filter
This results in a dictionary like this:
{
'rank': 'supreme_overlord',
'year_began__gt': 2000,
'year_end__lt': 3000
}
Use it like this:
qs = Researcher.objects.filter(**filter_kwargs)
Regarding the fields total_citations and h_index from ResearchMetrics, I assume you want to aggregate the values. So in your example above you want either a sum or an average.
The principle is the same:
from django.db.models import Sum
METRICS_FIELDS = ['total_citations', 'h_index']
annotate_kwargs = {}
for field in METRICS_FIELDS:
if not field in filter_in:
continue
annotated_field = '%s_sum' % field
annotate_kwargs[annotated_field] = Sum('researchmetric__%s' % field)
filter = filter_in[field]
if isinstance(filter, dict):
for filter_type, value in filter.items():
lookup_type = LOOKUP_MAPPING[filter_type]
lookup = '%s__%s' % (annotated_field, lookup_type)
filter_dict[lookup] = value
else:
filter_kwargs[field] = filter
Now your filter_kwargs look like this:
{
'h_index_sum__gt': 10,
'h_index_sum__lt': 20,
'rank': 'supreme_overlord',
'total_citations_sum__gt': 10,
'total_citations_sum__lt': 400,
'year_began__gt': 2000,
'year_end__lt': 3000
}
And your annotate_kwargs look like this:
{
'h_index_sum': Sum('reasearchmetric__h_index')),
'total_citations_sum': Sum('reasearchmetric__total_citations'))
}
So your final call looks like this:
Researcher.objects.annotate(**annotate_kwargs).filter(**filter_kwargs)
There are some assumptions in my answer, but I hope you get the general idea.
There is one important point: make sure you properly validate the input to make sure that only the field can be filtered that you want the user to filter. In my approach, this is ensured by hard coding the field names in RESEARCHER_FIELDS and METRICS_FIELDS.

Categories

Resources