Trouble with the Sal's Shipping project (Python 3)

Hi guys,

I’m wondering if a kind person from this community might be able to have a read through this code i wrote for the Sal’s shipping and explain why it is spitting out the wrong answers. Yes, I am SURE this is far from the most efficient way to write this function, however, I really strongly feel that if I were just able to understand why this method gives the wrong answer before I look at the project walkthrough it would help my learning A LOT and help me understand where my logic has gone wrong.

here’s the code - for some reason every time the function is called with a positive number input it returns Premium Ground Shipping as the cheapest. Yet when i follow the logic I can’t see how this should be the case most of the time. Negative numbers return Drone shipping as the cheapest.

def shipping_calculator_tool(weight):
  if (weight <= 2):
    #if ground shipping calc returns less than drone ship and prem ship:
    if str((weight*1.5)+20) < str(weight*4.5) and str((weight*1.5)+20) < str(125.00):
      return "The cheapest service is Ground shipping which will cost $" + str((weight*1.5)+20)
    if str((weight*1.5)+20) == str(weight*4.5) and str((weight*1.5)+20) < str(125.00):
      return "The cheapest service is either Ground or Drone shipping which both cost $" + str((weight*1.5)+20)
    if str((weight*1.5)+20) < str(weight*4.5) and str((weight*1.5)+20) == str(125.00):
      return "The cheapest service is either Ground or Premium Ground shipping which both cost $125.00"
    if str(weight*4.5) < str((weight*1.5)+20) and str(weight*4.5) < str(125.00):
      return "The cheapest service is Drone shipping which will cost $" + str(weight*4.5)
    if str(weight*4.5) < str((weight*1.5)+20) and str(weight*4.5) == str(125.00):
      return "The cheapest service is either Drone or Premium Ground shipping which will cost $125.00"
    if str(125.00) < str((weight*1.5)+20) and str(125.00) < str(weight*4.5):
      return "The cheapest service is Premium Ground Shipping which will cost $125.00"
    if str((weight*1.5)+20) == str(weight*4.5) and str((weight*1.5)+20) == str(125.00):
      return "All three shipping services cost the same. The grand total is $125.00"
    else:
      return "Invalid weight input."
      ###
      ###
      ### END OF BAND <= 2
  if (weight > 2) and (weight <= 6):
    #if ground shipping calc returns less than drone ship and prem ship:
    if str((weight*3)+20) < str(weight*9) and str((weight*3)+20) < str(125.00):
      return "The cheapest service is Ground shipping which will cost $" + str((weight*3)+20)
    if str((weight*3)+20) == str(weight*9) and str((weight*3)+20) < str(125.00):
      return "The cheapest service is either Ground or Drone shipping which both cost $" + str((weight*3)+20)
    if str((weight*3)+20) < str(weight*9) and str((weight*3)+20) == str(125.00):
      return "The cheapest service is either Ground or Premium Ground shipping which both cost $125.00"
    if str(weight*9) < str((weight*3)+20) and str(weight*9) < str(125.00):
      return "The cheapest service is Drone shipping which will cost $" + str(weight*9)
    if str(weight*9) < str((weight*3)+20) and str(weight*9) == str(125.00):
      return "The cheapest service is either Drone or Premium Ground shipping which will cost $125.00"
    if str(125.00) < str((weight*3)+20) and str(125.00) < str(weight*9):
      return "The cheapest service is Premium Ground Shipping which will cost $125.00"
    if str((weight*3)+20) == str(weight*9) and str((weight*3)+20) == str(125.00):
      return "All three shipping services cost the same. The grand total is $125.00"
    else:
      return "Invalid weight input."
      ###
      ###
      ### END OF BAND (weight > 2) and (weight <= 6) ^^^
      ###
      ###
  if (weight > 6) and (weight <= 10):
    #if ground shipping calc returns less than drone ship and prem ship:
    if str((weight*4)+20) < str(weight*12) and str((weight*4)+20) < str(125.00):
      return "The cheapest service is Ground shipping which will cost $" + str((weight*4)+20)
    if str((weight*4)+20) == str(weight*12) and str((weight*4)+20) < str(125.00):
      return "The cheapest service is either Ground or Drone shipping which both cost $" + str((weight*4)+20)
    if str((weight*4)+20) < str(weight*12) and str((weight*4)+20) == str(125.00):
      return "The cheapest service is either Ground or Premium Ground shipping which both cost $125.00"
    if str(weight*12) < str((weight*4)+20) and str(weight*12) < str(125.00):
      return "The cheapest service is Drone shipping which will cost $" + str(weight*12)
    if str(weight*12) < str((weight*4)+20) and str(weight*12) == str(125.00):
      return "The cheapest service is either Drone or Premium Ground shipping which will cost $125.00"
    if str(125.00) < str((weight*4)+20) and str(125.00) < str(weight*12):
      return "The cheapest service is Premium Ground Shipping which will cost $125.00"
    if str((weight*4)+20) == str(weight*12) and str((weight*4)+20) == str(125.00):
      return "All three shipping services cost the same. The grand total is $125.00"
    else:
      return "Invalid weight input."
      ###
      ###
      ### END OF BAND (weight > 6) and (weight <= 10) ^^^
      ###
      ###
  if (weight > 10):    
    if str((weight*4.75)+20) < str(weight*14.25) and str((weight*4.75)+20) < str(125.00):
      return "The cheapest service is Ground shipping which will cost $" + str((weight*4.75)+20)
    if str((weight*4.75)+20) == str(weight*14.25) and str((weight*4.75)+20) < str(125.00):
      return "The cheapest service is either Ground or Drone shipping which both cost $" + str((weight*4.75)+20)
    if str((weight*4.75)+20) < str(weight*14.25) and str((weight*4.75)+20) == str(125.00):
      return "The cheapest service is either Ground or Premium Ground shipping which both cost $125.00"
    if str(weight*14.25) < str((weight*4.75)+20) and str(weight*14.25) < str(125.00):
      return "The cheapest service is Drone shipping which will cost $" + str(weight*14.25)
    if str(weight*14.25) < str((weight*4.75)+20) and str(weight*14.25) == str(125.00):
      return "The cheapest service is either Drone or Premium Ground shipping which will cost $125.00"
    if str(125.00) < str((weight*4.75)+20) and str(125.00) < str(weight*14.25):
      return "The cheapest service is Premium Ground Shipping which will cost $125.00"
    if str((weight*4.75)+20) == str(weight*14.25) and str((weight*4.75)+20) == str(125.00):
      return "All three shipping services cost the same. The grand total is $125.00"
    else:
      return "Invalid weight input."
  else:
    return "Invalid weight input."
  ### END OF SHIPPING CALCULATOR FUNCTION

You could start by checking whether the correct condition gets satisfied

if some condition should have been satisfied, but wasn’t, then what values were involved in that condition?

Sorry what do you mean exactly by this?
For example if I type
print(shipping_calculator_tool(1.2))

it prints: “The cheapest service is Premium Ground Shipping which will cost $125.00”

whereas to my logic it should check if 1.2 < 2 which it is, and the only output in the <= 2 part of the function which gives “The cheapest service is Premium Ground Shipping which will cost $125.00” should return False since str(125.00) < str((1.2*1.5)+20) and str(125.00) < str(1.2*4.5) are both False

then you’ve narrowed things down a bit. now you would look at ONLY that part.

this is your new program to consider:

weight = 1.2
print(str(125.00) < str((weight*1.5)+20) and str(125.00) < str(weight*4.5))

now do the same thing again.

what should the individual results be in that, and what are they?

str(125.00) < str((weight*1.5)+20) is False since (1.2*1.5)+20 = 21.8 and 125 is not less than 21.8

str(125.00) < str(weight*4.5)) is False since 1.2*4.5 = 5.4 and 125 is not less than 5.4

So the program should result in False ? :sweat:

But if you run it it writes True. But which particular operation isn’t doing what you want? You can pick it apart into smaller parts.

You can also replace parts in the expression with what you think it should evaluate to. If you make a wrong replacement then the outcome will be different.

here’s a few such replacements.

weight = 1.2
print(str(125.00) < str((weight*1.5)+20) and str(125.00) < str(weight*4.5))
print("125.0" < str((weight*1.5)+20) and str(125.00) < str(weight*4.5))
print("125.0" < str((weight*1.5)+20) and str(125.00) < str(5.3999999999999995))
print("125.0" < str((weight*1.5)+20) and str(125.00) < "5.3999999999999995")

Ok, so I went in to the <= 2 part of the function and replaced

if str(125.00) < str((weight*1.5)+20) and str(125.00) < str(weight*4.5):

with

if "125.0" < str((weight*1.5)+20) and str(125.00) < str(weight*4.5):

if "125.0" < str((weight*1.5)+20) and str(125.00) < str(5.3999999999999995):

if "125.0" < str((weight*1.5)+20) and str(125.00) < "5.3999999999999995":

"125.0" < "21.8" and "125.00" < "5.3999999999999995":

All returned that Premium Ground Shipping is the cheapest when print(shipping_calculator_tool(1.2)) is called. Have I understood correctly what you were suggesting I do? Replace each of the elements of the expression? This seems to have no effect on what the function returns, although admittedly I have not yet tried all possible replacements for each part of the expression.

still got three operations left

<
and
<

Which of these two strings are smaller?

"aa" "z"

if they were in a dictionary, which comes first?

what about this?

"aa" "00"

this?

"50" "6"

this?

"125.0" "21.8"

oh dear,
instinctively
“aa” > “00”
“09” < “90”
and “125.0” > “21.8” :grimacing:

or am I wrong since size of a string is determined by the first alphanumeric character instead of how many alphanumeric characters? So in this case 1 < 2 therefore “125.0” is a smaller string than “21.8” ? if this is the case I learnt something new!

strings are compared lexicographically yeah.

the more important part here though - is to narrow down the problem

Ok, taking a break and coming back to this definitely helped me think more logically.

For shipping_calculator_tool(1.2) to return “The cheapest service is Premium Ground Shipping which will cost $125.00”, that means that the correct answer, which is that Drone Shipping is the cheapest, already had to write False. The issue cannot be confined to the one line of code I have been deliberating over.

So instead, I decided to replace operations in

if str(weight*4.5) < str((weight*1.5)+20) and str(weight*4.5) < str(125.00):
      return "The cheapest service is Drone shipping which will cost $" + str(weight*4.5)

To see if I could do anything to make this element of my function write true and generate the correct answer.

It turns out that I can, and the problem had arisen with me converting all numbers to strings. If I replace the above line of code with

if (weight*4.5) < (weight*1.5)+20 and (weight*4.5) < 125.00:

      return "The cheapest service is Drone shipping which will cost $" + str(weight*4.5)

Then the function returns
The cheapest service is Drone shipping which will cost $5.3999999999999995

So although I have resolved my initial problem I still have a couple of questions.

  1. What exactly was the reason why converting my operations into strings was causing me a problem here? Is it because strings are compared lexicographically as we discussed?

  2. How and why is the calculation str(weight*4.5) returning $5.3999999999999995 instead of 5.4?

  3. Once I have answered questions two and three, can I make my function:

  • More syntactically correct or efficient

  • Easier to understand / work with

  • Use the round function to limit the returned string number to two decimal places.

  1. numbers aren’t text
  2. …same answer as above, really. your expectations come from text manipulation, text is however not used to represent floats. floats are approximations, if you expect exact results then you meant something other than float, maybe you meant int (you could count cents), maybe you meant Fraction, or maybe you meant Decimal which does behave like text, ie it behaves like grade school math

you can make a function for each shipping method computing cost, apply each function to the weight and pick the smallest, you can for example use the built in min function… the minimal information for each option is a description and a price

rather than rounding, don’t approximate. float is often not what you want.

You could bundle together price and description for each method:

[
    (5, 'option1'),
    (3, 'option2'),
]

when you compare tuples (or lists for that matter), they will consider the first values first, and only move on to others if they are equal to break the tie

sorting that list will make (3, 'option2') come first, same if you use min or max, so if you picked the smallest entry from that list, you would get a price and a description which you could then print out

you would only get one result, so you can no longer say that there are two equal results. but you could find out what the smallest price is and then pick all options that have that price.
or maybe you’d have a second value with delivery speed or profit margin or maybe it just wouldn’t matter and you’d use whichever you got