'Formatting the contents of a list that is nested inside of a nested dictionary
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
Solution 1:[1]
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
