Last active
October 8, 2024 14:47
-
-
Save rjvitorino/abbbe5be143b0196552d30dca895a4f5 to your computer and use it in GitHub Desktop.
Cassidy's interview question of the week: a function that, given a list of ingredients needed for a recipe (represented as strings), and a list of ingredients available in the pantry, returns the minimum number of additional ingredients one needs to buy to make the recipe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections import Counter | |
from dataclasses import dataclass | |
from datetime import datetime, timedelta | |
from typing import List | |
@dataclass | |
class Ingredient: | |
""" | |
Class to represent an ingredient and its expiration date. | |
Attributes: | |
name (`str`): The name of the ingredient | |
expiration_date (`str`): Expiration date in "YYYY-MM-DD" format | |
""" | |
name: str | |
expiration_date: str | |
def is_expired(self) -> bool: | |
""" | |
Check if the ingredient is expired. | |
Returns: | |
`bool`: True if the ingredient is expired, False otherwise. | |
""" | |
expiration = datetime.strptime(self.expiration_date, "%Y-%m-%d") | |
return expiration < datetime.now() | |
def missing_ingredients(recipe: List[str], pantry: List[Ingredient]) -> int: | |
""" | |
Calculate the number of ingredients missing from the pantry to make a recipe. | |
Args: | |
recipe (`List[str]`): The list of ingredients needed for the recipe. | |
pantry (`List[Ingredient]`): A list of Ingredient objects representing pantry items. | |
Returns: | |
`int`: The minimum number of additional ingredients needed to buy. | |
""" | |
# Create a list of pantry items that are not expired | |
valid_pantry_items = [ | |
ingredient.name for ingredient in pantry if not ingredient.is_expired() | |
] | |
# Create counters for recipe and valid pantry items to account for quantities | |
recipe_counter = Counter(recipe) | |
pantry_counter = Counter(valid_pantry_items) | |
# Calculate missing ingredients by comparing the counts in the recipe to the counts in the pantry | |
missing_items = sum( | |
max(needed_qty - pantry_counter.get(ingredient, 0), 0) | |
for ingredient, needed_qty in recipe_counter.items() | |
) | |
return missing_items | |
def get_expiration_date(days_from_today: int) -> str: | |
""" | |
Generate an expiration date string relative to today. | |
Args: | |
days_from_today (`int`): The number of days from today for the expiration date. | |
Returns: | |
`str`: Expiration date in 'YYYY-MM-DD' format. | |
""" | |
return (datetime.today() + timedelta(days=days_from_today)).strftime("%Y-%m-%d") | |
def test_missing_ingredients(): | |
# Test 1: Basic test case, some items missing | |
recipe_1 = ["eggs", "flour", "sugar", "butter"] | |
pantry_1 = [ | |
Ingredient(name="sugar", expiration_date=get_expiration_date(60)), | |
Ingredient(name="butter", expiration_date=get_expiration_date(30)), | |
Ingredient(name="milk", expiration_date=get_expiration_date(-2)), | |
] | |
assert missing_ingredients(recipe_1, pantry_1) == 2, "Test 1 Failed" | |
# Test 2: All ingredients available, no missing items | |
recipe_2 = ["eggs", "flour", "sugar"] | |
pantry_2 = [ | |
Ingredient(name="eggs", expiration_date=get_expiration_date(10)), | |
Ingredient(name="flour", expiration_date=get_expiration_date(20)), | |
Ingredient(name="sugar", expiration_date=get_expiration_date(1)), | |
] | |
assert missing_ingredients(recipe_2, pantry_2) == 0, "Test 2 Failed" | |
# Test 3: All pantry items expired | |
recipe_3 = ["eggs", "milk"] | |
pantry_3 = [ | |
Ingredient(name="eggs", expiration_date=get_expiration_date(-10)), | |
Ingredient(name="milk", expiration_date=get_expiration_date(-5)), | |
] | |
assert missing_ingredients(recipe_3, pantry_3) == 2, "Test 3 Failed" | |
# Test 4: Some items expired, some still valid | |
recipe_4 = ["flour", "butter", "milk"] | |
pantry_4 = [ | |
Ingredient(name="flour", expiration_date=get_expiration_date(150)), | |
Ingredient(name="butter", expiration_date=get_expiration_date(20)), | |
Ingredient(name="milk", expiration_date=get_expiration_date(-2)), | |
] | |
assert missing_ingredients(recipe_4, pantry_4) == 1, "Test 4 Failed" | |
# Test 5: Empty recipe, no ingredients needed | |
recipe_5 = [] | |
pantry_5 = [ | |
Ingredient(name="sugar", expiration_date=get_expiration_date(30)), | |
Ingredient(name="butter", expiration_date=get_expiration_date(20)), | |
Ingredient(name="milk", expiration_date=get_expiration_date(5)), | |
] | |
assert missing_ingredients(recipe_5, pantry_5) == 0, "Test 5 Failed" | |
# Test 6: Empty pantry, need all recipe items | |
recipe_6 = ["eggs", "flour", "sugar"] | |
pantry_6 = [] | |
assert missing_ingredients(recipe_6, pantry_6) == 3, "Test 6 Failed" | |
# Test 7: Recipe with duplicate ingredients | |
recipe_7 = ["eggs", "flour", "eggs", "sugar"] | |
pantry_7 = [ | |
Ingredient(name="eggs", expiration_date=get_expiration_date(10)), | |
Ingredient(name="sugar", expiration_date=get_expiration_date(5)), | |
] | |
assert missing_ingredients(recipe_7, pantry_7) == 2, "Test 7 Failed" | |
print("All tests passed!") | |
test_missing_ingredients() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment