Using the code here, how would I change the value of stock for all the products with newstock?
products = {
"apple": {"price": 3.5, "stock": 134},
"banana": {"price": 6.82, "stock": 52},
"cake": {"price": 23, "stock": 5}
}
newstock = input("Enter amount to set :")
I assume a for loop like this would work
for x in products:
products []["stock"]=newstock
But I'm not sure what to put in the empty []
Here is what you want to do
products = {
"apple": {"price": 3.5, "stock": 134},
"banana": {"price": 6.82, "stock": 52},
"cake": {"price": 23, "stock": 5}
}
newstock = int(input("Enter amount to set :"))
for i in products.values():
i['stock'] = newstock
values() is a method that access your dict values that being the other dicts.
You can access a dict value by naming it is key inside brackets. You can loop through the multiple values and there you go.
You're close. Change
products []["stock"]=newstock
to
products[x]["stock"]=newstock
x is the dictionary key, you need to index the dictionary with it.
Related
I have a python dictionary where I am iterating it and creating a new dictionary with some calculations. My current code is working fine but I want to know what are the other different ways to do it?
The logic is-
1. if the same `room` has multiple `rate` codes then append it to that room.
2. get the lowest price for every room type.
Existing Code:
datas = [
{"code": "A1KXDY", "room": "A1K", "rate": "XDX", "price": 10},
{"code": "B1KXDY", "room": "B1K", "rate": "XDX", "price": 20},
{"code": "C1KXDY", "room": "C1K", "rate": "XDX", "price": 30},
{"code": "A1KXDY", "room": "A1K", "rate": "XDY", "price": 5},
{"code": "B1KXDY", "room": "B1K", "rate": "XDY", "price": 10},
{"code": "C1KXDY", "room": "C1K", "rate": "XDY", "price": 40},
]
final_result = {}
for data in datas:
display_rate = data["price"]
if data["room"] in final_result:
existing_room = final_result[data["room"]]
existing_room["rate_codes"].append(data["rate"])
current_rate = existing_room["display_rate"]
if current_rate > display_rate:
existing_room["display_rate"] = display_rate
continue
room_data = {"display_rate": display_rate, "rate_codes": [data["rate"]]}
final_result[data["room"]] = room_data
print(final_result)
Expected Output:
{'A1K': {'display_rate': 5, 'rate_codes': ['XDX', 'XDY']}, 'B1K': {'display_rate': 10, 'rate_codes': ['XDX', 'XDY']}, 'C1K': {'display_rate': 30, 'rate_codes': ['XDX', 'XDY']}}
You can use pandas for this.
import pandas as pd
datas = [...]
df = pd.DataFrame(datas)
df = df.groupby('room', as_index=False).agg({'price': min, 'rate': list})
df.rename(columns={'price': 'display_rate', 'rate': 'rate_codes'}, inplace=True)
result = df.to_dict('records')
Output:
[{'room': 'A1K', 'display_rate': 5, 'rate_codes': ['XDX', 'XDY']},
{'room': 'B1K', 'display_rate': 10, 'rate_codes': ['XDX', 'XDY']},
{'room': 'C1K', 'display_rate': 30, 'rate_codes': ['XDX', 'XDY']}]
The output can further be treated to match the output you want for final_result.
I would say your solution is fine. What constitutes "more smartly" is subjective to a large extent anyway.
I suppose, if you were willing to accept a set of rate_codes in the final result instead of a list, you could get away with fairly few lines of code:
final_result = {}
for data in datas:
room_key = data["room"] # just for readability
final_result.setdefault(room_key, {
"display_rate": data["price"],
"rate_codes": {data["rate"]}
})
final_result[room_key]["display_rate"] = min(
final_result[room_key]["display_rate"],
data["price"]
)
final_result[room_key]["rate_codes"].add(data["rate"])
Using the dict.setdefault method does nothing to final_result if it already has the key room_key; otherwise it inserts it with the value of that new dictionary.
We can use the min function instead of explicitly comparing values.
And we use the fact that a set always has unique values, so we can just call its add method without needing to check if that rate code already exists.
The result:
{'A1K': {'display_rate': 5, 'rate_codes': {'XDX', 'XDY'}}, 'B1K': {'display_rate': 10, 'rate_codes': {'XDX', 'XDY'}}, 'C1K': {'display_rate': 30, 'rate_codes': {'XDX', 'XDY'}}}
I am trying to find out how to return a sum of several values given in a order list of dictionaries
menu = {
1: {"name": 'espresso',
"price": 1.99},
2: {"name": 'coffee',
"price": 2.50},
3: {"name": 'cake',
"price": 2.79},
4: {"name": 'soup',
"price": 4.50},
5: {"name": 'sandwich',
"price": 4.99}
}
def calculate_subtotal(order):
return subtotal
def take_order():
display_menu()
order = []
count = 1
for i in range(3):
item = input('Select menu item number ' + str(count) + ' (from 1 to 5): ')
count += 1
order.append(menu[int(item)])
return order
def calculate_subtotal(order) should accept one argument which is the order list and return the sum
of the prices of the items in the order list.
Do I have to use a for loop to iterate through the values and sum each value?
How do I access the dictionaries inside the list?
Let's say a person orders the following:
orders = [
{"name": "espresso", "price": 1.99},
{"name": "espresso", "price": 1.99},
{"name": "soup", "price": 4.99},
]
You now have a list of dict. Indexing into the list returns a reference to a dictionary. For example:
first_order = orders[0]
print(first_order)
Would print:
{'name': 'espresso', 'price': 1.99}
Using that knowledge, you can loop through the orders like so:
total = 0.0
for order in orders:
total += order["price"]
Each order will be a dict like you saw above.
You can also use a comprehension if you're comfortable with them.
total = sum(order["price"] for order in orders)
I have a list of dictionaries look bellow
raw_list = [
{"item_name": "orange", "id": 12, "total": 2},
{"item_name": "apple", "id": 12},
{"item_name": "apple", "id": 34, "total": 22},
]
Expected output should be
[
{"item_name": ["orange", "apple"], "id": 12, "total": 2},
{"item_name": "apple", "id": 34, "total": 22},
]
but how i got
[
{"item_name": "orangeapple", "id": 12, "total": 2},
{"item_name": "apple", "id": 34, "total": 22},
]
Here my code bellow
comp_key = "id"
conc_key = "item_name"
res = []
for ele in test_list:
temp = False
for ele1 in res:
if ele1[comp_key] == ele[comp_key]:
ele1[conc_key] = ele1[conc_key] + ele[conc_key]
temp = True
break
if not temp:
res.append(ele)
how to resolve...?
Something like this - the special sauce is the isinstance stuff to make sure you're making the concatenated value a list instead.
Do note that this assumes the raw list is ordered by the comp_key (id), and will misbehave if that's not the case.
raw_list = [
{"item_name": "orange", "id": 12, "total": 2},
{"item_name": "apple", "id": 12},
{"item_name": "apple", "id": 34, "total": 22},
]
comp_key = "id"
conc_key = "item_name"
grouped_items = []
for item in raw_list:
last_group = grouped_items[-1] if grouped_items else None
if not last_group or last_group[comp_key] != item[comp_key]: # Starting a new group?
grouped_items.append(item.copy()) # Shallow-copy the item into the result array
else:
if not isinstance(last_group[conc_key], list):
# The concatenation key is not a list yet, make it so
last_group[conc_key] = [last_group[conc_key]]
last_group[conc_key].append(item[conc_key])
print(grouped_items)
import pandas as pd
df = pd.DataFrame(raw_list)
dd = pd.concat([df.groupby('id')['item_name'].apply(list), df.groupby('id').['total'].apply(sum)], axis=1).reset_index()
dd.to_dict('records')
you could use pandas to group by and apply function to two columns then convert to dict
[{'id': 12, 'item_name': ['orange', 'apple'], 'total': 2.0},
{'id': 34, 'item_name': ['apple'], 'total': 22.0}]
You could use itertools.grouper for grouping by id and collections.defaultdict to combine values with same keys into lists.
from itertools import groupby
from collections import defaultdict
id_getter = lambda x: x['id']
gp = groupby(sorted(raw_list, key=id_getter), key=id_getter)
out = []
for _,i in gp:
subdict = defaultdict(list)
for j in i:
for k,v in j.items():
subdict[k].append(v)
out.append(dict(subdict))
out
Working with complex datatypes such as nested lists and dictionaries, I would advice really utilizing the APIs provided by collections and itertools.
Lets say I have two lists:
list1 = [{"sport": 'Soccer',
"leagues": [{"id": 1001,
"name": "League1",
"events": [{"id": 100,
"home": "team1",
"away": "team2"},
{"id": 101,
"home": "team3",
"away": "team4"}]}]},
{"sport": 'Basketball',
"leagues": [{"id": 1002,
"name": "League2",
"events": [{"id": 200,
"home": "team5",
"away": "team6"},
{"id": 201,
"home": "team7",
"away": "team8"}]},
{"id": 1003,
"name": "League3",
"events": [{"id": 300,
"home": "team9",
"away": "team10"},
{"id": 301,
"home": "team11",
"away": "team12"}]}],
}
]
list2 = [{"sport": 'Soccer',
"leagues": [{"id": 1001,
"events": [{"id": 100,
"odds": {"home": 1.862, "away": 1.847}},
{"id": 101,
"odds": {"home": 1.70, "away": 2.10}}]}]},
{"sport": 'Basketball',
"leagues": [{"id": 1002,
"events": [{"id": 200,
"odds": {"home": 1.952, "away": 1.952}},
{"id": 201,
"odds": {"home": 1.90, "away": 2.05}}]},
{"id": 1003,
"events": [{"id": 300,
"odds": {"home": 1.5, "away": 2.7}},
{"id": 301,
"odds": {"home": 1.75, "away": 2.09}}]}]}]
I would like to combine events from list1 with their respective "odds" from list2.
In reality there are many more elements in these two lists, the example is simplified for the sake of clarity.
My current (ugly) solution:
for sport_list1 in list1:
for sport_list2 in list2:
if sport_list1['sport'] == sport_list2['sport']:
for league_list1 in sport_list1['leagues']:
for league_list2 in sport_list2['leagues']:
if league_list1['id'] == league_list2['id']:
for event_list1 in league_list1['events']:
for event_list2 in league_list2['events']:
if event_list1['id'] == event_list2['id']:
print(sport_list1['sport'], league_list1['name'], event_list1['home'], event_list1['away'], event_list2['odds'])
break
Desired output:
Soccer League1 team1 team2 {'home': 1.862, 'away': 1.847}
Soccer League1 team3 team4 {'home': 1.7, 'away': 2.1}
Basketball League2 team5 team6 {'home': 1.952, 'away': 1.952}
Basketball League2 team7 team8 {'home': 1.9, 'away': 2.05}
Basketball League3 team9 team10 {'home': 1.5, 'away': 2.7}
Basketball League3 team11 team12 {'home': 1.75, 'away': 2.09}
Any way to make this cleaner and/or more efficient?
#Chronial answer is perfect but here is another approach you might find interesting:
def convert_to_dicts(x):
if type(x) == list:
id_field = {"sport", "id"}.intersection(set(x[0].keys())).pop()
return {y.pop(id_field): convert_to_dicts(y) for y in x}
elif type(x) == dict:
return{z: convert_to_dicts(y) for z, y in x.items()}
return x
def recursive_dict_merge(x, y):
new_dict = {}
for key in set(x.keys()).union(set(y.keys())):
x_val = x.get(key, None)
y_val = y.get(key, None)
if type(x_val) == dict and type(y_val) == dict:
new_dict[key] = recursive_dict_merge(x_val, y_val)
else:
new_dict[key] = x_val or y_val
return new_dict
result = recursive_dict_merge(convert_to_dicts(list1), convert_to_dicts(list2))
I first convert the "list of dict of lists of dicts" into just nested dictionaries.
Then I use recursion to merge those dictionaries.
I think this approach is better because you have an "easy-to-use" result dictionary which then makes it easier to do other things, e.g. the exact print that you want:
for sport, leagues in result.items():
for league in leagues["leagues"].values():
for event in league["events"].values():
print(sport, league['name'], event['home'], event['away'], event['odds'])
In general for problems like this, I find the best first step is usually to reshape the inputs into something more manageable. Nested dictionaries are much easier to think about than lists of dicts of lists of dicts of... etc.
You can write a small helper function. Using dicts frees of you of the O(n²).
def zip_by_key(key, list1, list2):
map1 = {x[key]: x for x in list1}
map2 = {x[key]: x for x in list2}
for k in map1.keys() & map2.keys():
yield (map1[k], map2[k])
Then your code will be:
for sport1, sport2 in zip_by_key('sport', list1, list2):
for league1, league2 in zip_by_key('id', sport1['leagues'], sport2['leagues']):
for event1, event2 in zip_by_key('id', league1['events'], league2['events']):
print(sport1['sport'], league1['name'], event1['home'], event1['away'], event2['odds'])
If I have a dictionary containing a list, like this:
{"items": [{"name": "Orange", "cost": 5}, {"name": "Apple", "cost": 10}]}
Would it be possible to get the keys "name" and "cost" from both lists in some way?
It's just for the reason that I don't want to add another line every time I add something new to the dictionary in the same list.
I am assuming that, by getting the keys you meant the values associated with those keys.
a['items'] is a list. Just iterate through it and access the name and cost values as you would access any dictionary.
>>> a_dict = {"items": [{"name": "Orange", "cost": 5}, {"name": "Apple", "cost": 10}]}
>>> for a_d in a_dict['items']:
print 'Name: {} Cost: {}'.format(a_d['name'], a_d['cost'])
This gives:
Name: Orange Cost: 5
Name: Apple Cost: 10
If infact you wanted the key names of the dictionaries in the list a['items'] you can get them using .keys():
>>> for a_d in a_dict['items']:
print a_d.keys()
This gives:
['cost', 'name']
['cost', 'name']
The best way to avoid code duplication is to use a function.
def get_name_and_cost(d):
return d['name'], d['cost']
for d in lst['items']:
name, cost = get_name_and_cost(d)
print(name, cost)
If you have more than one group of 'items', this will go through each group in your dictionary.
d = {"fruit": [{"name": "Orange", "cost": 5}, {"name": "Apple", "cost": 10}],
"vegetables": [{"name": "carrot", "cost": 5}, {"name": "eggplant", "cost": 10}]}
for group, items in d.items():
for item in items:
print item['name']
>>>
carrot
eggplant
Orange
Apple
I think a more dynamic, reusable approach would be to make a function.
def get_values(d, *keys):
t = []
for k in keys:
if k in d.keys():
t.append(d[k])
else:
t.append(None)
return t
or
def get_values(d, *keys):
return [ d[k] if k in d.keys() else none for k in keys]
then you can do stuff like this!
>>> d = {"items": [{"name": "Orange", "cost": 5}, {"name": "Apple", "cost": 10}]}
>>> for item in d["items"]:
... print "Name {}, Cost {}".format(*get_values(item, "name", "cost"))
...
Name Orange, Cost 5
Name Apple, Cost 10