so I have a list of stores and their inventory and a shopping list. I need to find the minimum number of stores to satisfy the shopping list. I currently have separated the store name to create all permutations of the stores. I am not sure how to actually make the comparison of stores inventory to the shopping list I have. Any help would be greatly appreciated.
def satisfy_shopping_list(shopping_list_json, inventory_json):
# find out minimum combination of stores that would satisfy shopping list
shops = []
print(inventory_json['stores'])
for item in inventory_json['stores']:
shops.append(item.get("name"))
routes = list(itertools.permutations(shops))
print(routes)
# if shopping list is impossible to satisfy
shopping_list_satisfiable = True
if shopping_list_satisfiable:
# print out number of stores and corresponding combinations
# num_stores = 0
# print "The shopping list can be satisfied by visiting {} store(s):".format(num_stores)
# for each valid store_combination:
# print_store_list(store_combination)
pass
else:
print("No combination of given stores can satisfy this shopping list :(")
pass
def print_store_combination(store_combination):
store_combination_copy = copy.deepcopy(store_combination)
store_combination_copy.sort()
print(', '.join(store_combination_copy))
def main():
args = parse_args()
with open(args.shopping_list_json_path) as shopping_list_json_file, open(args.inventory_json_path) as inventory_json_file:
shopping_list_json = json.load(shopping_list_json_file)
inventory_json = json.load(inventory_json_file)
satisfy_shopping_list(shopping_list_json, inventory_json)
def parse_args():
p = argparse.ArgumentParser()
p.add_argument('shopping_list_json_path')
p.add_argument('inventory_json_path')
args = p.parse_args()
return args
if __name__ == '__main__':
main()
Example Shopping List
{
"apples": 10,
"oranges": 10,
"pineapples": 10,
"coconuts": 10,
"strawberries": 10,
"peaches": 1
}
Inventory Example
{
"stores": [
{
"name":"Kroger",
"inventory": {
"oranges": 10,
"coconuts": 10,
"strawberries": 10
}
},
{
"name":"Meijer",
"inventory": {
"oranges": 10,
"grapes": 10,
"pineapples": 10,
"strawberries": 10
}
},
{
"name":"Store 3",
"inventory": {
"apples": 1,
"oranges": 10,
"bananas": 10,
"grapes": 10,
"chickens": 10
}
},
{
"name":"Whole Foods",
"inventory": {
"grapes": 10,
"pineapples": 10,
"organic apples": 10,
"coconuts": 10,
"strawberries": 10
}
},
{
"name":"Kroger 2",
"inventory": {
"apples": 8
}
},
{
"name":"peach store",
"inventory": {
"peaches": 1
}
},
{
"name":"CVS",
"inventory": {}
},
{
"name":"apples r us",
"inventory": {
"apples": 10000000000000
}
}
]
}
Here's a rough solution from me - just by prioritizing shops that are able to satisfy your shopping list most. Then you just iterate and trim the shopping list from there. I'm leaving the satisfiability check of the shopping list to you. You should do the check before jumping into the iteration I believe, by comparing the aggregate of the inventory against the numbers required in the shopping list.
def satisfy_shopping_list(shopping_list_json, inventory_json):
# find out minimum combination of stores that would satisfy shopping list
shops = []
inventory = [] # De-nesting the shops and their inventories. Matched by index
for item in inventory_json['stores']:
shops.append(item.get("name"))
inventory.append(item.get("inventory"))
# if shopping list is impossible to satisfy
shopping_list_satisfiable = True # You need to do this check yourself
shop_seq = []
if shopping_list_satisfiable:
while len(shopping_list_json) > 0:
# Compute satisfiability score for all shops
# We assign a score of 1 for each item that the shop is able to satisfy fully
# Else, we give it a pro-rated score if the shop has the supplies
scorelist = []
for i, ivt in enumerate(inventory):
score= 0
for k, v in shopping_list_json.items():
if k in ivt.keys():
if ivt[k] >= v:
score += 1
else:
score += (ivt[k]/v)
scorelist.append((score, i))
# Go to the shop with the highest score first
scorelist.sort(key=itemgetter(0), reverse=True)
shop_seq.append(shops[scorelist[0][1]])
# Update shopping list
shop_idx = scorelist[0][1]
to_remove = []
for k, v in shopping_list_json.items():
if k in inventory[shop_idx].keys():
if v - inventory[shop_idx][k] <= 0:
to_remove.append(k)
else:
shopping_list_json[k] = v - inventory[shop_idx][k]
for rem in to_remove:
shopping_list_json.pop(rem)
print(shop_seq)
else:
print("No combination of given stores can satisfy this shopping list :(")
pass
Related
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)
Given a list of dictionaries:
data = {
"data": [
{
"categoryOptionCombo": {
"id": "A"
},
"dataElement": {
"id": "123"
}
},
{
"categoryOptionCombo": {
"id": "B"
},
"dataElement": {
"id": "123"
}
},
{
"categoryOptionCombo": {
"id": "C"
},
"dataElement": {
"id": "456"
}
}
]
}
I would like to display the dataElement where the count of distinct categoryOptionCombo is larger than 1.
e.g. the result of the function would be an iterable of IDs:
[123]
because the dataElement with id 123 has two different categoryOptionCombos.
tracker = {}
for d in data['data']:
data_element = d['dataElement']['id']
coc = d['categoryOptionCombo']['id']
if data_element not in tracker:
tracker[data_element] = set()
tracker[data_element].add(coc)
too_many = [key for key,value in tracker.items() if len(value) > 1]
How can I iterate the list of dictionaries preferably with a comprehension? This solution above is not pythonic.
One approach:
import collections
counts = collections.defaultdict(set)
for d in data["data"]:
counts[d["dataElement"]["id"]].add(d["categoryOptionCombo"]["id"])
res = [k for k, v in counts.items() if len(v) > 1]
print(res)
Output
['123']
This approach creates a dictionary mapping dataElements to the different types of categoryOptionCombo:
defaultdict(<class 'set'>, {'123': {'B', 'A'}, '456': {'C'}})
Almost a one-liner:
counts = collections.Counter( d['dataElement']['id'] for d in data['data'] )
print( counts )
Output:
Counter({'123': 2, '456': 1})
No need for sets, you can just remember each data element's first coc or mark it as having 'multiple'.
tracker = {}
for d in data['data']:
data_element = d['dataElement']['id']
coc = d['categoryOptionCombo']['id']
if tracker.setdefault(data_element, coc) != coc:
tracker[data_element] = 'multiple'
too_many = [key for key,value in tracker.items() if value == 'multiple']
(If the string 'multiple' can be a coc id, then use multiple = object() and compare with is).
I am trying to iterate through a nested dictionary. I want to display all the values associated with the key: "city_name". This is my piece of code.
nested_dictionary = {"responseCode": 0,
"responseDesc": [
{
"city_id": 1,
"city_name": "Mumbai",
"total_trips": 0
},
{
"city_id": 2,
"city_name": "Delhi",
"total_trips": 0
}
]
}
temp = "city_name"
for i in nested_dictionary.keys():
print(i)
if i == "responseDesc":
x = [v[temp] for k, v in nested_dictionary.items() if temp in v]
print("The extracted values : " + str(x))
Each time i try to run it throws the type error. I am not able to figure out where is the value of x becoming an integer?
Any help would be appreciated. Thanks in advance.
You are trying to access to whole dict instead of included list of cities "responseDesc"
I think you was going to get somethinkg like this:
nested_dictionary = {"responseCode": 0,
"responseDesc": [
{
"city_id": 1,
"city_name": "Mumbai",
"total_trips": 0
},
{
"city_id": 2,
"city_name": "Delhi",
"total_trips": 0
}
]
}
temp = "city_name"
x = [desc[temp] for desc in nested_dictionary['responseDesc'] if temp in desc]
print("The extracted values : " + str(x))
This is what you need. You need to fetch the value using the key.
x = [v[temp] for v in nested_dictionary[i] if temp in v]
print("The extracted values : " + str(x))
I am looking for a dynamic approach to solve my issue. I have a very complex structure, but for simplicity,
I have a dictionary structure like this:
dict1={
"outer_key1" : {
"total" : 5 #1.I want the value of "total"
},
"outer_key2" :
[{
"type": "ABC", #2. I want to count whole structure where type="ABC"
"comments": {
"nested_comment":[
{
"key":"value",
"id": 1
},
{
"key":"value",
"id": 2
}
] # 3. Count Dict inside this list.
}}]}
I want to this iterate dictionary and solve #1, #2 and #3.
My attempt to solve #1 and #3:
def getTotal(dict1):
#for solving #1
for key,val in dict1.iteritems():
val = dict1[key]
if isinstance(val, dict):
for k1 in val:
if k1=='total':
total=val[k1]
print total #gives output 5
#for solving #3
if isinstance(val,list):
print len(val[0]['comment']['nested_comment']) #gives output 2
#How can i get this dynamicallty?
Output:
total=5
2
Que 1 :What is a pythonic way to get the total number of dictionaries under "nested_comment" list ?
Que 2 :How can i get total count of type where type="ABC". (Note: type is a nested key under "outer_key2")
Que 1 :What is a pythonic way to get the total number of dictionaries under "nested_comment" list ?
User Counter from the standard library.
from collections import Counter
my_list = [{'hello': 'world'}, {'foo': 'bar'}, 1, 2, 'hello']
dict_count = Counter([x for x in my_list if type(x) is dict])
Que 2 :How can i get total count of type where type="ABC". (Note: type is a nested key under "outer_key2")
It's not clear what you're asking for here. If by "total count", you are referring to the total number of comments in all dicts where "type" equals "ABC":
abcs = [x for x in dict1['outer_key2'] if x['type'] == 'ABC']
comment_count = sum([len(x['comments']['nested_comment']) for x in abcs])
But I've gotta say, that is some weird data you're dealing with.
You got answers for #1 and #3, check this too
from collections import Counter
dict1={
"outer_key1" : {
"total" : 5 #1.I want the value of "total"
},
"outer_key2" :
[{
"type": "ABC", #2. I want to count whole structure where type="ABC"
"comments": {
"nested_comment":[
{
"key":"value",
"key": "value"
},
{
"key":"value",
"id": 2
}
] # 3. Count Dict inside this list.
}}]}
print "total: ",dict1['outer_key1']['total']
print "No of nested comments: ", len(dict1['outer_key2'][0]['comments'] ['nested_comment']),
Assuming that below is the data structure for outer_key2 this is how you get total number of comments of type='ABC'
dict2={
"outer_key1" : {
"total" : 5
},
"outer_key2" :
[{
"type": "ABC",
"comments": {'...'}
},
{
"type": "ABC",
"comments": {'...'}
},
{
"type": "ABC",
"comments": {'...'}
}]}
i=0
k=0
while k < len(dict2['outer_key2']):
#print k
if dict2['outer_key2'][k]['type'] == 'ABC':
i+=int(1)
else:
pass
k+=1
print ("\r\nNo of dictionaries with type = 'ABC' : "), i
I am trying to convert a list of dot-separated strings, e.g.
['one.two.three.four', 'one.six.seven.eight', 'five.nine.ten', 'twelve.zero']
into a tree (nested lists or dicts - anything that is easy to walk through).
The real data happens to have 1 to 4 dot-separated parts of different length and has 2200 records in total.
My actual goal is to fill in the set of 4 QComboBox'es with this data, in manner that the 1st QComboBox is filled with first set items ['one', 'five', 'twelve'] (no duplicates). Then depending on the chosen item, the 2nd QComboBox is filled with its related items: for 'one' it would be: ['two', 'six'], and so on, if there's another nested level.
So far I've got a working list -> nested dicts solution, but it's horribly slow, since I use regular dict(). And I seem to have a trouble to redesign it to a defaultdict in a way to easily work out filling the ComboBoxes properly.
My current code:
def list2tree(m):
tmp = {}
for i in range(len(m)):
if m.count('.') == 0:
return m
a = m.split('.', 1)
try:
tmp[a[0]].append(list2tree(a[1]))
except (KeyError, AttributeError):
tmp[a[0]] = list2tree(a[1])
return tmp
main_dict = {}
i = 0
for m in methods:
main_dict = list2tree(m)
i += 1
if (i % 100) == 0: print i, len(methods)
print main_dict, i, len(methods)
ls = ['one.two.three.four', 'one.six.seven.eight', 'five.nine.ten', 'twelve.zero']
tree = {}
for item in ls:
t = tree
for part in item.split('.'):
t = t.setdefault(part, {})
Result:
{
"twelve": {
"zero": {}
},
"five": {
"nine": {
"ten": {}
}
},
"one": {
"six": {
"seven": {
"eight": {}
}
},
"two": {
"three": {
"four": {}
}
}
}
}
While this is beyond the reach of the original question, some comments mentioned a form of this algorithm that incorporates values. I came up with this to that end:
def dictionaryafy(self, in_dict):
tree = {}
for key, value in in_dict.items():
t = tree
parts = key.split(".")
for part in parts[:-1]:
t = t.setdefault(part, {})
t[parts[-1]] = value
return tree