ReplitUser
ReplitUser

Reputation: 87

Shorten if-elif-elif...-else

I have a program, and one function is to sell an item that the user owns. It prompts the user to input the name (id) and amount, and it sells. But there are a lot of items the user can own, so there are lots of if else elif statements. How do I shorten this? (P.S. I am using Replit, and Replit currently has Python 3.8) Here is the sell function, for reference.

def sell_command():
  global cash
  cash = 0

  #I created a dictionary, inventory, which has how much the user has of a particular item.

  #itemSell variable contains what the user wants to sell
  #itemSellCount variable contains how much the user wants to sell
  #itemSoldCash variable calculates how much one item is worth, and multiplies for how much the user is selling
  #cash variable is hlobal since another function prints cash
  
  itemSell = input('What would you like to sell? ')
  itemSell = itemSell.lower()

  
  if itemSell == "cobblestone" or "cobble stone":
    itemSellCount = int(input("How many would you like to sell? "))
    if itemSellCount <= inventory["cobblestone"]:
      itemSoldCash = itemSellCount*10
      print("You sold " + str(itemSellCount) + " cobblestone/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["cobblestone"] -= itemSellCount
    elif itemSellCount > inventory["cobblestone"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "coal":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["coal"]:
      itemSoldCash = itemSellCount*5
      print("You sold " + str(itemSellCount) + " coal for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["coal"] -= itemSellCount
    elif itemSellCount > inventory["coal"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "iron ore" or "ironore":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["ironOre"]:
      itemSoldCash = itemSellCount*20
      print("You sold " + str(itemSellCount) + " iron ore/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["cobblestone"] -= itemSellCount
    elif itemSellCount > inventory["ironOre"]:
      print("You tried to sell more than what you have!")
    elif itemSell == "iron ingot" or "ironingot":
      itemSellCount = int(input("How many would you like to sell?"))
      if itemSellCount <= inventory["ironIngot"]:
        itemSoldCash = itemSellCount*25
        print("You sold " + str(itemSellCount) + " iron ingot/s for $" + str(itemSoldCash))
        cash = cash + itemSoldCash
        inventory["ironIngot"] -= itemSellCount
      elif itemSellCount > inventory["ironIngot"]:
        print("You tried to sell more than what you have!")
  elif itemSell == "emerald" or "emeralds":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["emerald"]:
      itemSoldCash = itemSellCount*100
      print("You sold " + str(itemSellCount) + "emerald/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["emerald"] -= itemSellCount
    elif itemSellCount > inventory["emerald"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "diamond" or "diamonds":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["diamond"]:
      itemSoldCash = itemSellCount*300
      print("You sold " + str(itemSellCount) + " diamond/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["diamond"] -= itemSellCount
    elif itemSellCount > inventory["diamond"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "oak":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["oak"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " oak/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["oak"] -= itemSellCount
    elif itemSellCount > inventory["oak"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "birch":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["birch"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " birch for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["birch"] -= itemSellCount
    elif itemSellCount > inventory["birch"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "redwood" or "red wood":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["redwood"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + "redwood for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["redwood"] -= itemSellCount
    elif itemSellCount > inventory["redwood"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "spruce":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["spruce"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " spruce for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["spruce"] -= itemSellCount
    elif itemSellCount > inventory["spruce"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "acacia":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["acacia"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " acacia for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["acacia"] -= itemSellCount
    elif itemSellCount > inventory["acacia"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "jungle":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["jungle"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " jungle for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["jungle"] -= itemSellCount
    elif itemSellCount > inventory["jungle"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "maple":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["maple"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " maple for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["maple"] -= itemSellCount
    elif itemSellCount > inventory["maple"]:
      print("You tried to sell more than what you have!")

Upvotes: 0

Views: 447

Answers (4)

Marion
Marion

Reputation: 198

That's a lot of duplicate code for what is, essentially, the same thing (I've cleaned up your syntax a bit):

itemSellCount = int(input("How many would you like to sell? "))
if itemSellCount <= inventory[itemSell]:
  itemSoldCash = itemSellCount*10
  print(f"You sold {itemSellCount} {itemSell}/s for ${itemSoldCash}")
  cash += itemSoldCash
  inventory[itemSell] -= itemSellCount
else:
  print("You tried to sell more than what you have!")

There are, however, three things to consider:

1. How much does each item sell for?

This can be addressed in a number of ways, depending on the programming style, and what you need to track about each item. An OOP approach would be to make an item class, with each item having some attribute defining its value. A straightforward, procedural approach, would be to have a dictionary that defines this:

itemValue = {
    "cobblestone": 10,
    "coal": 5,
    ...
}

Now, use a dictionary lookup to determine itemSoldCash:

itemSoldCash = itemSellCount*itemValue[itemSell]

2. Alternative Item Names

You accept alternative item names, e.g. "cobble stone" is treated as "cobblestone." This can also be approached with a dictionary, e.g. something like:

itemAltNames = {
    "cobble stone": "cobblestone",
    "iron ingot": "iron ingot",
    ...
}

Then, you can do something like:

if itemSell in itemAltNames:
    itemSell = itemAltNames[itemSell];

Alternatively, if your alternatives only involve stripping spaces, then just do so:

itemSell = itemSell.replace(" ","")

3. Checking that the Item Exists

As it stands, your control flow won't execute if the user enters an invalid item. This is good, but overcomplicated! Also, do you give an error message (or allow repeated input) if the user enters an invalid item? Check against your inventory dictionary to ensure that the user has the item:

if itemSell in inventory:

Putting it All Together

Here's what everything might look like now:

def sell_command():

    global cash
    cash = 0

    itemSell = input("What would you like to sell? ")
    while (itemSell := itemSell.lower().strip().replace(" ","")) not in inventory:
        itemSell = input(f"You do not have {itemSell} in your inventory. What would you like to sell? ")

    itemSellCount = int(input("How many would you like to sell? "))
    if itemSellCount <= inventory[itemSell]:
        itemSoldCash = itemSellCount * itemValue[itemSell]
        print(f"You sold {itemSellCount} {itemSell}/s for ${itemSoldCash}")
        cash += itemSoldCash
        inventory[itemSell] -= itemSellCount
    else:
        print("You tried to sell more than what you have!")

Upvotes: 1

Glauco
Glauco

Reputation: 1463

You can organize the code a little bit better:

the

itemSellCount = int(input("How many would you like to sell?")

is done for each items, this can be done one time, and after that the

itemSoldCash = itemSellCount*15

is the quantity for costs. Costs can be organized into a catalog in a better data structure, maybe something like this:

# you can add if needed other attributes for all material, like color, weight, damage, duration, and so on
catalogue = {
    "cobblestone": {"cost": 15, 'color':'red'},
    "maple": {"cost": 15},
    "jungle": {"cost": 15},
    "acacia": {"cost": 15},
    "diamond": {"cost": 300},
    ...
}

# aliases
catalogue["cobble stone"] = catalogue["cobblestone"]


def sell(item, qty, inventory):
    if qty <= inventory[item]:
        sold = qty * catalogue[item]["cost"]
        print("You sold {} maple for ${}".format(qty, sold))
        cash += sold
        inventory[item] -= qty
    else:
        print("You tried to sell more {} than what you have!".format(item))

and finally you call this function with all needed information:

itemSell = input('What would you like to sell? ')
itemSell = itemSell.lower()
itemSellCount = int(input("How many would you like to sell? "))
sell(itemSell,itemSellCount, inventory)

inventory is not defined in the example.

PS if many objects costs the same, and they are considered default costs, you can simplify the catalogue definition putting in it only special cost, default can be applied as missing value:

sold = qty * (catalogue[item]["cost"] if item in catalogue else 15)

Upvotes: 0

StilltheSmartie
StilltheSmartie

Reputation: 53

This code is probably the easiest fix. You might have to change the inventory dictionary though, so that every key is lowercase.

Also, itemSell == "cobblestone" or "cobble stone" always returns true.

def sell_command():
  global cash
  cash = 0
  cashMultiplierDict = {"cobblestone": 10, "coal": 5} #etc
  #The following line just removes all white spaces (e.g. cobble stone becomes cobblestone)
  itemSell = input('What would you like to sell? ').lower().replace(" ", "")
  itemSellCount = int(input("How many would you like to sell? "))
  if itemSellCount <= inventory[itemSell]:
    itemSoldCash = itemSellCount * cashMultiplierDict[itemSell] #Uses the dict created before
    print("You sold " + str(itemSellCount) + " " + itemSell + "/s for $" + str(itemSoldCash))
    cash = cash + itemSoldCash
    inventory[itemSell] -= itemSellCount
  elif itemSellCount > inventory[itemSell]:
    print("You tried to sell more than what you have!")

Upvotes: 0

Kris
Kris

Reputation: 8873

You can reduce the branching a lot using dictionaries and objects for what you are doing. And it is the power of OOPs. Here is an example how you will remodel your code.

cash = 0


class CommodityShelf:
    """
    A shelf to hold commodities
    """
    def __init__(self, item_name, available_quantity, price):
        self.item_name = item_name
        self.available_qty = available_quantity
        self.price = price or 10

    def key(self):
        # "make it easy to search for this shelf"
        return self.item_name.lower().replace(" ", "")

    def sell(self):
        # handle the selling logic at one place, for any item
        global cash
        sell_units = int(input(f"How many {self.item_name} would you like to sell? "))
        if sell_units > self.available_qty:
            print(f"You tried to sell more {self.item_name} than what you have!")
        else:
            cash += (sell_units * self.price)
            self.available_qty -= sell_units
            print(f"You sold {self.item_name} for ${sell_units * self.price}")


def start_selling(pos_registry):
    """
    Start selling commodities
    :param pos_registry:
    :return:
    """
    item_to_sell = input('What would you like to sell? ').lower().replace(" ", "")
    if item_to_sell in pos_registry:
        pos_registry[item_to_sell].sell()
    else:
        print(f"You tried to sell {item_to_sell} but it's not in your inventory!")


if __name__ == '__main__':
    # create a shelf for each commodity
    inventory = [
        CommodityShelf("Cobble Stone", 10, 0),
        CommodityShelf("Coal", 10, 0),
        CommodityShelf("Iron Ore", 10, 0),
        CommodityShelf("Iron Ingot", 10, 0),
        CommodityShelf("Emarald", 10, 0),
        CommodityShelf("Diamond", 10, 0),
    ]
    # create a registry of all the shelves
    pos_registry = {pos.key(): pos for pos in inventory}

    # start selling
    start_selling(pos_registry)
    
    # loop it if the user wants to
    

I hope the logical explanation is in the comments.

Upvotes: 0

Related Questions