Script in Javascript to automate meal planning in order to hit daily calories and protein.
-
-
Save jshuadvd/e312f3e152ad9df271cd4fc532776a42 to your computer and use it in GitHub Desktop.
Meal Planner
This file contains hidden or 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
module.exports = [ | |
{ | |
_key: 'MEXICAN_BURGERS', | |
_id: 'recipes/MEXICAN_BURGERS', | |
_rev: '_XTqNHIW--_', | |
name: 'MEXICAN BURGERS', | |
Calories: 150, | |
Fat: 5, | |
Carbs: 1, | |
Protein: 23, | |
type: 'Meal', | |
servings: 4, | |
}, | |
{ | |
_key: 'CHOCOLATE_CHIP_FROZEN_YOGURT_ICE_CREAM', | |
_id: 'recipes/CHOCOLATE_CHIP_FROZEN_YOGURT_ICE_CREAM', | |
_rev: '_XTqNfbK--_', | |
name: 'CHOCOLATE CHIP FROZEN YOGURT ICE CREAM', | |
Calories: 519, | |
Protein: 10, | |
Carbs: 86, | |
Fat: 17, | |
type: 'Snack', | |
servings: 1, | |
}, | |
{ | |
_key: 'BROILED_OMELET', | |
_id: 'recipes/BROILED_OMELET', | |
_rev: '_XTqN2pe--_', | |
name: 'BROILED OMELET', | |
Calories: 668, | |
Protein: 92, | |
Carbs: 4, | |
Fat: 31, | |
type: 'Meal', | |
servings: 1, | |
}, | |
{ | |
_key: 'CRISPY_CHICKEN_BITES', | |
_id: 'recipes/CRISPY_CHICKEN_BITES', | |
_rev: '_XTqOIhK--_', | |
name: 'CRISPY CHICKEN BITES', | |
Calories: 510, | |
Protein: 75, | |
Carbs: 0, | |
Fat: 4, | |
type: 'Meal', | |
servings: 1, | |
}, | |
{ | |
_key: 'MONTREAL_STYLE_SIRLOIN_ROAST', | |
_id: 'recipes/MONTREAL_STYLE_SIRLOIN_ROAST', | |
_rev: '_XTqOVK2--_', | |
name: 'MONTREAL STYLE SIRLOIN ROAST', | |
Calories: 632, | |
Protein: 103, | |
Carbs: 0, | |
Fat: 21, | |
type: 'Meal', | |
servings: 1, | |
}, | |
{ | |
_key: 'EMILYS_RUSSET_POTATO_WEDGES', | |
_id: 'recipes/EMILYS_RUSSET_POTATO_WEDGES', | |
_rev: '_XTqOtN---_', | |
name: 'EMILY’S RUSSET POTATO WEDGES', | |
Calories: 170, | |
Fat: 7, | |
Carbs: 26, | |
Protein: 3, | |
type: 'Side', | |
servings: 2, | |
}, | |
{ | |
_key: 'CHEESE_QUESADILLAS', | |
_id: 'recipes/CHEESE_QUESADILLAS', | |
_rev: '_XTqPAP6--_', | |
name: 'CHEESE QUESADILLAS', | |
Calories: 448, | |
Protein: 26, | |
Carbs: 37, | |
Fat: 22, | |
type: 'Side', | |
servings: 1, | |
}, | |
{ | |
_key: 'SALMON_WITH_DILL', | |
_id: 'recipes/SALMON_WITH_DILL', | |
_rev: '_XTqPPWe--_', | |
name: 'SALMON WITH DILL', | |
Calories: 160, | |
Fat: 8, | |
Carbs: 0, | |
Protein: 23, | |
type: 'Meal', | |
servings: 4, | |
}, | |
{ | |
_key: 'CHICKEN_ENCHILADA_BAKE', | |
_id: 'recipes/CHICKEN_ENCHILADA_BAKE', | |
_rev: '_XTqPmXu--_', | |
name: 'CHICKEN ENCHILADA BAKE', | |
Calories: 310, | |
Fat: 8, | |
Carbs: 29, | |
Protein: 34, | |
type: 'Meal', | |
servings: 8, | |
}, | |
{ | |
_key: 'CRISPY_FLANK_STEAK', | |
_id: 'recipes/CRISPY_FLANK_STEAK', | |
_rev: '_XTqP7GK--_', | |
name: 'CRISPY FLANK STEAK', | |
Calories: 715, | |
Protein: 96, | |
Carbs: 0, | |
Fat: 33, | |
type: 'Meal', | |
servings: 1, | |
}, | |
{ | |
_key: 'PATSY_POTATOES', | |
_id: 'recipes/PATSY_POTATOES', | |
_rev: '_XTqQQIG--_', | |
name: 'PATSY POTATOES', | |
Calories: 590, | |
Protein: 11, | |
Carbs: 90, | |
Fat: 23, | |
type: 'Side', | |
servings: 1, | |
}, | |
{ | |
_key: 'GREGS_RUSSET_POTATO_WEDGES', | |
_id: 'recipes/GREGS_RUSSET_POTATO_WEDGES', | |
_rev: '_XTqQhru--_', | |
name: 'GREG’S RUSSET POTATO WEDGES', | |
Calories: 512, | |
Protein: 11, | |
Carbs: 90, | |
Fat: 14, | |
type: 'Side', | |
servings: 1, | |
}, | |
]; |
This file contains hidden or 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
const { List } = require('immutable'); | |
const data = require('./data'); | |
// find the combinations of recipes that amount total calories and protein requirements | |
const BODY_WEIGHT_LBS = 180; | |
const TOTAL_CALORIES = 3200; | |
const CALORIES_LOWER_BOUND = TOTAL_CALORIES - 50; | |
const CALORIES_UPPER_BOUND = TOTAL_CALORIES + 50; | |
const PROTEIN_UPPER_BOUND = 1 * BODY_WEIGHT_LBS; | |
function calculateDayMenu({ | |
menu = List([]), | |
recipes = List([]), | |
recipeIndex = 0, | |
calories = 0, | |
protein = 0, | |
}) { | |
const shouldExit = recipeIndex >= recipes.size; | |
if (shouldExit) { | |
const shouldRestart = calories < CALORIES_LOWER_BOUND; | |
if (shouldRestart) { | |
return calculateDayMenu({ | |
recipes: List(data.sort(randomSort)), | |
}); | |
} | |
return { menu, calories, protein }; | |
} | |
const recipe = recipes.get(recipeIndex) || {}; | |
const caloriesCounted = calories + recipe.Calories; | |
const proteinCounted = protein + recipe.Protein; | |
const remainingServings = recipe.servings - 1; | |
const shouldSkipRecipe = | |
caloriesCounted > CALORIES_UPPER_BOUND || | |
proteinCounted > PROTEIN_UPPER_BOUND || | |
recipe.servings === 0; | |
if (shouldSkipRecipe) { | |
return calculateDayMenu({ | |
menu, | |
recipes, | |
recipeIndex: recipeIndex + 1, | |
calories, | |
protein, | |
}); | |
} | |
const newMenuItem = `${recipe.Calories}cal | protein ${recipe.Protein}g | ${ | |
recipe.name | |
} | ${recipe.type}`; | |
const recipeUpdated = { ...recipe, servings: remainingServings }; | |
return calculateDayMenu({ | |
menu: menu.push(newMenuItem), | |
recipes: recipes.update(recipeIndex, () => recipeUpdated), | |
recipeIndex, | |
calories: caloriesCounted, | |
protein: proteinCounted, | |
}); | |
} | |
function randomSort() { | |
if (Math.random() > 0.5) return 1; | |
if (Math.random() > 0.5) return -1; | |
return 0; | |
} | |
const dayMenu = calculateDayMenu({ | |
recipes: List(data.sort(randomSort)), | |
}); | |
console.log(JSON.stringify(dayMenu, null, 4)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment