Skip to content

Shopping Lists and Aggregation

Mike Christensen edited this page Aug 28, 2026 · 1 revision

Shopping lists and aggregation

Shopping lists can combine recipes, normalized ingredients, explicit usages, and arbitrary text. Database-backed lists belong to the current context identity.

Aggregate without persistence

For browser-local or desktop state, keep source entries in the application and call the engine only for parsing and aggregation:

var parsed = new[] { "12 eggs", "a cup of milk" }
   .Select(context.ParseIngredientUsage)
   .Where(result => result.Usage is not null)
   .Select(result => result.Usage)
   .ToArray();

var items = context.AggregateIngredients(parsed);
var fromRecipes = context.AggregateRecipes(recipeIds);

AggregateRecipes loads the requested recipes from the database if the modeler capability is off. This makes a parsing-only context a good profile for a simple recipe site.

Persist the default list

var result = context.ShoppingLists
   .Update(ShoppingList.Default)
   .AddItems(items => items
      .AddRecipe(Recipe.FromId(recipeId))
      .AddIngredient(Ingredient.FromId(ingredientId))
      .AddUsage(usage)
      .AddItem("12 bananas")
      .AddItem("paper towels"))
   .Commit();

Recognized raw strings are normalized and aggregated. Unrecognized strings remain raw shopping-list entries, which is important for a real grocery list.

Named lists

var created = context.ShoppingLists.Create
   .WithName("Weekend groceries")
   .AddItems(items => items.AddRecipe(Recipe.FromId(recipeId)))
   .Commit();

var lists = context.ShoppingLists.LoadAll.WithItems.List();

ShoppingListResult.NewShoppingListId identifies a new list and ShoppingListResult.List contains the normalized result.

Update items

context.ShoppingLists
   .Update(list)
   .UpdateItem(item, update => update.CrossOut)
   .UpdateItem(otherItem, update => update.NewAmount(new Amount(3, Units.Cup)))
   .RemoveItem(unneededItem)
   .Commit();

Item identity matters: an aggregate can represent several sources. If the UI needs undo, per-source purchase state, or deterministic removal, retain original entries separately and rebuild the aggregate after changes. The Shopping List TUI is a complete database-free example; the Web App keeps its sources in browser storage.

Raw .AddItem(...) operations require IngredientParsing in a DB context. Adding already-normalized usages does not require NLP.

Clone this wiki locally