-1

I have a nested dictionary that stores recipes that a user inputs. Within the nested dictionary I have a list. I would like to find a way to neatly display the contents of the list as I am displaying the contents of the nested dictionary. Please see the code samples below as well as the output I would like to receive.

def build_ingredients(self):
    """Creates a list of ingredients based off user input"""
    next_ingredient = ''
    while True:
        next_ingredient = self.ask_input("Please enter an ingredient for this recipe or type done to continue ")
        if next_ingredient.lower() == 'done':
            break
        self.ingredients.append(next_ingredient)

def add_recipe(self): 
        """Adding a recipie to the recipie book"""
        recipie_name = self.ask_input("Please enter the name of the recipie that you are adding: ").title()
        category = self.ask_input("Please enter the category you would like this recipie to be a part of: ").title()
        preptime = self.ask_input("Please enter the amount of time it will take to prepare this recipe: ").title()
        cooktime = self.ask_input("Please enter the amount of time in minutes it will take to complete this recipie (please include minutes or hours): ").title()
        self.build_ingredients()
        instructions = self.ask_input("Please list out the instructions to making this recipie: ").title()
        print('____________________________________________________________________________________________________________')
        if self.recipes[str(1)] == {'Recipe Name' : '' , 'Category' : '' ,'Prep Time' : '' , 'Cooktime' : '' , 'Ingredients' : '' , 'Instructions' : ''}:
            self.recipes[str(1)] = {'Recipe Name' : f'{recipie_name}', 'Category' : f'{category}' , 'Preptime': f'{preptime}', 'Cooktime' : f'{cooktime}' , 'Ingredients' : f'{self.ingredients}' , 'Instructions' : f'{instructions}'}
            self.save_data()
        else:   
            self.recipes[str(len(self.recipes) + 1)] = {'Recipe Name' : f'{recipie_name}', 'Category' : f'{category}' ,'Preptime': f'{preptime}', 'Cooktime' : f'{cooktime}' , 'Ingredients' : f'{self.ingredients}' , 'Instructions' : f'{instructions}'}
            self.save_data()

def view_selected_recipe(self):
    """once the user determines what recipe they would like to view, display it in a neat format"""
    while True:
        try:
            recipe_choice = self.ask_input("\nPlease enter the number that corresponds to the recipe you would like to view: ")
            if recipe_choice in self.number_list:
                break
            print("Please enter a number")
        except Exception as e:
            print(e)
        print("\n")
    for key, value in self.recipes[str(recipe_choice)].items(): 
        if f"{key}" == 'Ingredients':
            for item in self.ingredients:
                print(f"\t{item}")
        print(f"\t{key} : {value}")

The output that I am seeing is:

Recipe Name : Pbj
Category : Snack
Preptime : 1
Cooktime : 1
Ingredients : ['pb', 'j', 'bread']
Instructions : Spread And Eat

The output that I would like to see is:

Recipe Name : Pbj
Category : Snack
Preptime : 1
Cooktime : 1
Ingredients:
              pb
              j
              bread
Instructions: spread and eat
ddejohn
  • 8,775
  • 3
  • 17
  • 30
Raluvas
  • 15
  • 5
  • Can you please provide a minimal reproducible example? There's a lot going on in this code and it's not clear how you're using it. For example, why are you casting the `key` to `str`? It's also not clear why you'd get `['pb', 'j', 'bread']` printed if you indeed entered the ingredients correctly. – ddejohn Apr 23 '22 at 19:25
  • For instance, when I convert your given data to a simple dictionary and run your code, I get a completely different output. Note that no matter what, on every iteration, you print the key-value pair present in the recipe... So even if you print the ingredients properly, you'll also get the ingredients printed as a list right after it. – ddejohn Apr 23 '22 at 19:34
  • Try going through your program with a [debugger](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems#:~:text=A%20debugger%20is%20a%20program,while%20your%20program%20is%20running.). It looks like your condtional statement that checks for if the key is ``Ingredients`` may not be entering. – Alias Cartellano Apr 23 '22 at 19:34

1 Answers1

0

I'm not able to reproduce your output, using a simple dictionary as a stand-in for your class:

In [2]: recipe
Out[2]:
{'Recipe Name': 'Pbj',
 'Category': 'Snack',
 'Preptime': 1,
 'Cooktime': 1,
 'Ingredients': ['pb', 'j', 'bread'],
 'Instructions': 'Spread And Eat'}

In [3]: for key, value in recipe.items():
   ...:     if key == 'Ingredients':
   ...:         for item in value:
   ...:             print(f"\t{item}")
   ...:     print(f"\t{key} : {value}")
   ...:
        Recipe Name : Pbj
        Category : Snack
        Preptime : 1
        Cooktime : 1
        pb
        j
        bread
        Ingredients : ['pb', 'j', 'bread']
        Instructions : Spread And Eat

Note that no matter what, on every iteration, you print the key-value pair present in the recipe... so even if you print the ingredients properly, you'll also get the ingredients printed as a list right after it, as shown in the output above.

Instead, I'd suggest adding a tab any time the value is an iterable object, and formatting the items as an indented, numbered list:

In [4]: for key, value in recipe.items():
   ...:     if type(value) in (dict, list, set, tuple):
   ...:         print(f"{key}:")
   ...:         for idx, item in enumerate(value, 1):
   ...:             print(f"\t{idx}. {item}")
   ...:         continue
   ...:     print(f"{key}: {value}")
   ...:
Recipe Name: Pbj
Category: Snack
Preptime: 1
Cooktime: 1
Ingredients:
        1. pb
        2. j
        3. bread
Instructions: Spread And Eat

Note that if you stored the instructions in a list as well, you'd get the same formatting:

In [7]: for key, value in recipe.items():
   ...:     if type(value) in (dict, list, set, tuple):
   ...:         print(f"{key}:")
   ...:         for idx, item in enumerate(value, 1):
   ...:             print(f"\t{idx}. {item}")
   ...:         continue
   ...:     print(f"{key}: {value}")
   ...:
Recipe Name: Pbj
Category: Snack
Preptime: 1
Cooktime: 1
Ingredients:
        1. pb
        2. j
        3. bread
Instructions:
        1. Spread
        2. Eat

Now, a numbered list may not be appropriate for a list of ingredients, and this is where you might want to design a custom class for any part of a Recipe which contains multiple items (like a list of ingredients, or numbered steps for instructions), and then implementing __str__:

class RecipeSubItem:
    def __init__(self, *items, bullet_style="-", indent_level=0):
        self.items = items
        self.bullet_style = bullet_style
        self.indent_level = indent_level

    def item_formatter(self):
        if self.bullet_style == "numbered":
            return ((f"{i}.", item) for i, item in enumerate(self.items, 1))
        return ((self.bullet_style, item) for item in self.items)

    def __str__(self):
        indent = "\t" * self.indent_level
        return "\n".join(f"{indent}{bullet} {item}" for bullet, item in self.item_formatter())

Demo:

In [9]: ingredients = RecipeSubItem("peanut butter", "jelly", "bread")

In [10]: print(ingredients)
- peanut butter
- jelly
- bread

In [11]: ingredients.bullet_style = "*"

In [12]: ingredients.indent_level = 1

In [13]: print(ingredients)
        * peanut butter
        * jelly
        * bread

In [14]: instructions = RecipeSubItem("slice bread", "lay bread on plate", "spread peanut butter on one bread slice", "spread jelly on other bread slice", bullet_style="numbered", indent_level=0)

In [15]: print(instructions)
1. slice bread
2. lay bread on plate
3. spread peanut butter on one bread slice
4. spread jelly on other bread slice
ddejohn
  • 8,775
  • 3
  • 17
  • 30