Dictionary values and list values within a function

Viewed 85

I have a dictionary with product names and prices:

products = {'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}

And a list with amounts of each product:

amounts = [3, 0, 5, 1, 3, 2, 0]

I want to get an output shown there total price of that order.

Not using functions I seem to get it right:

products = {'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}
amounts = [3, 0, 5, 1, 3, 2, 0]
res_list = []
order = []

for value in products.values():
    res_list.append(value)

for i in range(0, len(res_list)):
    order.append(amounts[i] * res_list[i])
    total = sum(order)

print(res_list)
print(order) #this line and the one above are not really necessary 
print(total)

Output : 63

But when I try using this code within a function I am having some problems. this is what I have tried:

products = {'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}
amounts = [3, 0, 5, 1, 3, 2, 0]
#order = []
def order(prod):
  res_list = []
  for value in prod.values():
    res_list.append(value)
  return res_list

prices = order(products)
print(prices)

def order1(prices):
  order =[]
  for i in range(0, len(prices)):
    order.append(amounts[i] * prices[i])
    total = sum(order)
    return total

print(order1(prices))

Not working the way it is intended.

Thanks for all the help I am learning.

5 Answers

The immediate problem is that your lines:

    total = sum(order)
    return total

are indented too much, so that they are inside the for loop. Outside of a function, the bug does not matter too much, because all that happens is that the total is recalculated on every iteration but the final value is the one that is used. But inside the function, what will happen is that it will return on the first iteration.

Reducing the indentation so that it is outside the for loop will fix this.

def order1(prices):
  order =[]
  for i in range(0, len(prices)):
    order.append(amounts[i] * prices[i])
  total = sum(order)
  return total

However, separate from that, you are relying on the order within the dictionary, which is only guaranteed for Python 3.7 and more recent. If you want to allow the code to be run reliably on earlier versions of Python, you can use an OrderedDict.

from collections import OrderedDict

products = OrderedDict([('a', 2), ('b', 3), ('c', 4), ('d', 5),
                        ('e', 6), ('f', 7), ('g', 8)])

Incidentally, your order function is unnecessary. If you want to convert products.values() (a dictionary values iterator) to a list, just use:

prices = list(products.values())

Also, in order1 it is unnecessary to build up an order list and sum it - you could use:

    total = 0
    for i in range(0, len(prices)):
        total += amounts[i] * prices[i]

That is probably enough to be getting on with for now, but if you wish to make a further refinement, then look up about how zip is used, and think how it could be used with your loop over amounts and prices.

Just zip products.values() and amounts, find the product of each pair, and then finally sum the result

>>> products = {'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}
>>> amounts = [3, 0, 5, 1, 3, 2, 0]
>>> 
>>> sum(i*j for i,j in zip(products.values(), amounts))
63

You can do this.

products = {'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}
amounts = [3, 0, 5, 1, 3, 2, 0]
def order(products, amounts):

    res_list = []
    order = []

    for value in products.values():
        res_list.append(value)

    for i in range(0, len(res_list)):
        order.append(amounts[i] * res_list[i])
        total = sum(order)

    print(res_list)
    print(order) #this line and the one above are not really necessary 
    print(total)
    
    return(total)

order(products, amounts)

You don't really need to iterate twice assuming that the amount of items in products and in amounts is the same.

products = {'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}
amounts = [3, 0, 5, 1, 3, 2, 0]
    
def order(products: dict, amounts: list):
    total = 0
    for idx, (_key, val) in enumerate(products.items()):
        total = total + amounts[idx] * val

    return total

print(order(products, amounts))

Note: The order of the items in the dictionary is not guaranteed, you might want to look into different data structures that link together products and amounts in a better way, i.e.:

products = {'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}
amounts = {'a': 3, 'b': 0, 'c': 5, 'd': 1, 'e': 3, 'f': 2, 'g': 0}

In this way you could do this:

def order(products: dict, amounts: dict):
    total = 0
    for key, val in products.items():
        total = total + val * amounts[key]

    return total

print(order(products, amounts))

Once we're at it, let's get fancy with numpy, since in the end, you just want the dot product prices x amounts:

import numpy as np
total = np.dot(list(products.values()), amounts)

63

But seriously, I'd strictly use either lists or dicts for both datasets, not mix them up, since that can seriously cause problems with order syncronisation between them, even if you are on Python 3.7 with the changes made there as mentioned.

Related