Home > Back-end >  Shorten if-elif-elif...-else
Shorten if-elif-elif...-else

Time:12-31

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!")

CodePudding user response:

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!")

CodePudding user response:

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.

CodePudding user response:

It appears a lot of your if-else is redundant. The only thing that is unique in each if-condition is itemSoldCash. You can use a helper function to map them to the relevant itemSells and use a single if-else condition otherwise:

def sell_command():
    global cash
    cash = 0
    
    def get_Cash(itemSell, itemSellCount):
        if itemSell == "cobblestone":
            return itemSellCount*10
        elif itemSell == "coal":
            return itemSellCount*5
        elif itemSell == "ironore":
            return itemSellCount*20
        elif itemSell == "ironingot":
            return itemSellCount*25
        elif itemSell == "emerald":
            return itemSellCount*100
        elif itemSell == "diamond":
            return itemSellCount*300
        elif itemSell in ["oak","birch","redwood","spruce","acacia","jungle","maple"]:
            return itemSellCount*15
  
    itemSell = ''.join(input('What would you like to sell? ').lower())
    itemSellCount = int(input("How many would you like to sell? "))
    
    if itemSellCount <= inventory[itemSell]:
        itemSoldCash = get_Cash(itemSell, itemSellCount)
        print("You sold {} {} for ${}".format(itemSellCount, itemSell, itemSoldCash))
        cash  = itemSoldCash
        inventory[itemSell] -= itemSellCount
    else:
        print("You tried to sell more than what you have!")

    

CodePudding user response:

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!")

CodePudding user response:

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)
  • Related