Machine Coding Problem

Food Recipe App

macoAlllifestyleunit-conversionfiltering
Commonly Asked By:YummlyTastyPinterest

Requirements & System Scope

Functional Scope (In-Scope)

  • Ingredient Density Unit Conversion: Converts fluid volume into metric weight utilizing dynamic density tables (e.g. converting sugar, flour, and milk accurately).
  • Dynamic Servings Scaling Engine: Adjusts raw volume metrics based on target-to-base ratio and performs decimal fraction formatting.
  • Dietary Tag Classification Matching: Restricts options strictly by filtering against specific dietary profiles (e.g. Vegetarian, Vegan, Gluten-Free).
  • Ingredient Overlap Recommendation Scoring: Analyzes a user's local pantry list to sort matching recipes by matching ratios.

Explicit Boundaries (Out-of-Scope)

  • Direct Grocery Order Delivery Integrations: Ignores external merchant APIs, payment checkouts, and routing logistics.
  • Active Recipe Social Sharing Boards: Excludes live user forums, comment systems, or photo attachment storage grids.

Class Diagram & Entity Relationships

Structural layout showing relationships between recipes, ingredient measurements, and conversions:

Loading...
  • Ingredient Class: Encapsulates compound metrics (quantity, specific unit type, name reference).
  • UnitConverter Service: Static translation matrix evaluating cross-family volumes and weights dynamically.

Design Patterns & SOLID Principles

  • Strategy Pattern: Encapsulates separate unit conversion strategies (intra-family ratio shifts vs cross-family density translation formulas).
  • Open/Closed Principle (OCP): The system permits extending unit conversions or introducing new ingredient types without modifying the core parsing routines.
  • Dependency Inversion Principle (DIP): The recipe matchmaking processor relies on high-level set operations rather than low-level database structural details.

Core Execution Workflows

Serving Adjustments & Conversion Workflows

  1. Cross-Family Weight/Volume Conversion:
    1. Identify if source and target units reside in different classifications.
    2. Fetch density factor assigned to the unique matching ingredient keyword name.
    3. Divide source weight by density to obtain milliliters, or multiply volume by density to output grams.
    4. Calculate target metric ratios and round to appropriate decimals.
  2. Pantry Match Ranking:
    1. Acquire the catalog list of available pantry ingredients from user profiles.
    2. Examine total ingredients required within the target recipe.
    3. Compute the ratio of matches to total dependencies, and sort options in descending ranking order.

Concurrency & Thread Safety Strategy

Implementing responsive recipe browsing under high volume user operations:

  • Stateless Search Calculations: Executes pantry overlap filtering as pure, side-effect-free calculations on immutable catalog references to ensure seamless multithreading.
  • Immutable Scaling Contexts: Generates new instances of ingredient objects during serving-size alterations instead of mutating standard database arrays.

Complete Clean Code Blueprint

Production reference implementations demonstrating ingredient density translations and recipe scaling in Java and Python:

// โ”€โ”€โ”€ JAVA BLUEPRINT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

enum Unit {
    GRAMS("Weight", 1.0),
    KILOGRAMS("Weight", 1000.0),
    MILLILITERS("Volume", 1.0),
    LITERS("Volume", 1000.0),
    CUPS("Volume", 240.0),
    TABLESPOON("Volume", 15.0),
    TEASPOON("Volume", 5.0);

    private final String type;
    private final double baseFactor; // Factor to convert to base unit (Grams or Milliliters)

    Unit(String type, double baseFactor) {
        this.type = type;
        this.baseFactor = baseFactor;
    }

    public String getType() { return type; }
    public double getBaseFactor() { return baseFactor; }
}

enum DietaryTag {
    VEGETARIAN, VEGAN, GLUTEN_FREE, KETO, NUT_FREE
}

class Ingredient {
    private final String name;
    private final double quantity;
    private final Unit unit;

    public Ingredient(String name, double quantity, Unit unit) {
        this.name = name;
        this.quantity = quantity;
        this.unit = unit;
    }

    public String getName() { return name; }
    public double getQuantity() { return quantity; }
    public Unit getUnit() { return unit; }

    @Override
    public String toString() {
        return String.format("%.2f %s of %s", quantity, unit, name);
    }
}

class Recipe {
    private final String recipeId;
    private final String title;
    private final int baseServings;
    private final List<Ingredient> ingredients;
    private final List<String> instructions;
    private final Set<DietaryTag> dietaryTags;

    public Recipe(String recipeId, String title, int baseServings, List<Ingredient> ingredients, List<String> instructions, Set<DietaryTag> dietaryTags) {
        this.recipeId = recipeId;
        this.title = title;
        this.baseServings = baseServings;
        this.ingredients = ingredients;
        this.instructions = instructions;
        this.dietaryTags = dietaryTags;
    }

    public String getRecipeId() { return recipeId; }
    public String getTitle() { return title; }
    public int getBaseServings() { return baseServings; }
    public List<Ingredient> getIngredients() { return ingredients; }
    public List<String> getInstructions() { return instructions; }
    public Set<DietaryTag> getDietaryTags() { return dietaryTags; }

    @Override
    public String toString() {
        return "Recipe{title='" + title + "', tags=" + dietaryTags + "}";
    }
}

class UnitConverter {
    // Density table for cross-family conversions: IngredientName -> Density (grams per milliliter)
    private static final Map<String, Double> densities = new HashMap<>();

    static {
        densities.put("flour", 0.53);  // 0.53g per ml
        densities.put("sugar", 0.84);  // 0.84g per ml
        densities.put("milk", 1.03);   // 1.03g per ml
        densities.put("water", 1.00);  // 1.00g per ml
    }

    public static Ingredient convert(Ingredient ingredient, Unit targetUnit) {
        if (ingredient.getUnit() == targetUnit) {
            return ingredient;
        }

        double val = ingredient.getQuantity();
        Unit sourceUnit = ingredient.getUnit();
        String name = ingredient.getName().toLowerCase();

        // 1. Convert within the same family (e.g. Weight -> Weight)
        if (sourceUnit.getType().equals(targetUnit.getType())) {
            double valueInBase = val * sourceUnit.getBaseFactor();
            double targetValue = valueInBase / targetUnit.getBaseFactor();
            return new Ingredient(ingredient.getName(), targetValue, targetUnit);
        }

        // 2. Cross-family conversion using density (Volume <-> Weight)
        double density = densities.getOrDefault(name, 1.0); // Fallback density
        double volumeInMl;

        if ("Weight".equals(sourceUnit.getType())) {
            // Weight to Volume: grams / density = ml
            double grams = val * sourceUnit.getBaseFactor();
            volumeInMl = grams / density;
        } else {
            // Volume to Weight: ml * density = grams
            double ml = val * sourceUnit.getBaseFactor();
            volumeInMl = ml * density; // This is weight in grams
        }

        // Convert base value of destination family to target unit
        double targetVal = volumeInMl / targetUnit.getBaseFactor();
        return new Ingredient(ingredient.getName(), targetVal, targetUnit);
    }
}

class RecipeService {
    private final List<Recipe> recipes = new CopyOnWriteArrayList<>();

    public void addRecipe(Recipe recipe) {
        recipes.add(recipe);
    }

    public List<Ingredient> scaleRecipe(Recipe recipe, int targetServings) {
        double ratio = (double) targetServings / recipe.getBaseServings();
        return recipe.getIngredients().stream()
                .map(i -> new Ingredient(i.getName(), roundToSensibleFraction(i.getQuantity() * ratio), i.getUnit()))
                .collect(Collectors.toList());
    }

    private double roundToSensibleFraction(double value) {
        return Math.round(value * 100.0) / 100.0;
    }

    // Pantry matching score resolver
    public List<Recipe> findRecipesByIngredients(Set<String> availableIngredients, Set<DietaryTag> dietaryFilters) {
        Set<String> lowerAvailable = availableIngredients.stream()
                .map(String::toLowerCase)
                .collect(Collectors.toSet());

        return recipes.stream()
                .filter(r -> r.getDietaryTags().containsAll(dietaryFilters))
                .sorted((r1, r2) -> {
                    double score1 = getMatchScore(r1, lowerAvailable);
                    double score2 = getMatchScore(r2, lowerAvailable);
                    return Double.compare(score2, score1); // Descending score
                })
                .collect(Collectors.toList());
    }

    private double getMatchScore(Recipe recipe, Set<String> lowerAvailable) {
        if (recipe.getIngredients().isEmpty()) return 0.0;
        
        long matches = recipe.getIngredients().stream()
                .map(i -> i.getName().toLowerCase())
                .filter(lowerAvailable::contains)
                .count();

        return (double) matches / recipe.getIngredients().size();
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== STARTING FOOD RECIPE APP SIMULATION ===");
        RecipeService service = new RecipeService();

        // Create mock recipes
        List<Ingredient> cakeIngredients = List.of(
            new Ingredient("flour", 2.0, Unit.CUPS),
            new Ingredient("sugar", 200.0, Unit.GRAMS),
            new Ingredient("milk", 1.0, Unit.CUPS)
        );
        Recipe sweetCake = new Recipe("r1", "Sweet Cake", 4, cakeIngredients, List.of("Mix", "Bake"), Set.of(DietaryTag.VEGETARIAN));

        List<Ingredient> breadIngredients = List.of(
            new Ingredient("flour", 3.0, Unit.CUPS),
            new Ingredient("water", 1.5, Unit.CUPS)
        );
        Recipe veganBread = new Recipe("r2", "Vegan Bread", 6, breadIngredients, List.of("Knead", "Rise", "Bake"), Set.of(DietaryTag.VEGETARIAN, DietaryTag.VEGAN, DietaryTag.NUT_FREE));

        service.addRecipe(sweetCake);
        service.addRecipe(veganBread);

        // 1. Density-based conversion testing
        System.out.println("--- Density-Based Converter Test ---");
        Ingredient sugarInCups = new Ingredient("sugar", 2.0, Unit.CUPS);
        Ingredient sugarInGrams = UnitConverter.convert(sugarInCups, Unit.GRAMS);
        System.out.println("2 Cups of Sugar converted to Grams: " + sugarInGrams); // Uses density: 2 * 240 * 0.84 = 403.2g

        Ingredient milkInGrams = new Ingredient("milk", 500.0, Unit.GRAMS);
        Ingredient milkInCups = UnitConverter.convert(milkInGrams, Unit.CUPS);
        System.out.println("500g of Milk converted to Cups: " + milkInCups); // Uses density

        // 2. Scaling recipe servings
        System.out.println("\n--- Recipe Ingestion Scaling Test ---");
        System.out.println("Scaling Sweet Cake from 4 servings to 12 servings:");
        List<Ingredient> scaled = service.scaleRecipe(sweetCake, 12);
        for (Ingredient ing : scaled) {
            System.out.println("  " + ing);
        }

        // 3. Pantry Match Matching Test
        System.out.println("\n--- Pantry-based Match Ranking ---");
        Set<String> pantry = Set.of("flour", "water");
        System.out.println("Searching vegetarian recipes matching pantry items: " + pantry);
        List<Recipe> matched = service.findRecipesByIngredients(pantry, Set.of(DietaryTag.VEGETARIAN));
        for (Recipe r : matched) {
            System.out.println("  Matched Recipe: " + r.getTitle());
        }

        System.out.println("=== FOOD RECIPE APP SIMULATION COMPLETE ===");
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...