Я пытаюсь решить проблему, где мне нужно рассчитать пищевую ценность рецепта, такого как «Мой бутерброд».
У меня есть база данных с «рецептами» и «ингредиентами» и необходимым количеством в кг.
рецепт может содержать ингредиенты, но также и другие рецепты, которые также могут содержать рецепты/ингредиенты.
Это пример «списка деталей» «Тестового бутерброда» общим весом 0,095 кг.
array (
0 =>
array (
'id' => 2,
'name' => 'Test Cheese',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.015,
'depth' => 1,
),
1 =>
array (
'id' => 3,
'name' => 'Test Ham',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.03,
'depth' => 1,
),
2 =>
array (
'id' => 4,
'name' => 'Test Sesam Bread',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.05, // Only use some of the test bread (with added Sesam)
'depth' => 1,
'parts' =>
array (
0 =>
array (
'id' => 5,
'name' => 'Test Bread',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 1.0, // This a recipe for 1 kg of banana bread
'depth' => 2,
'parts' =>
array (
0 =>
array (
'id' => 6,
'name' => 'Test Flour',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.5,
'depth' => 3,
),
1 =>
array (
'id' => 7,
'name' => 'Test Water',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.3,
'depth' => 3,
),
2 =>
array (
'id' => 8,
'name' => 'Test Yeast',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.05,
'depth' => 3,
),
3 =>
array (
'id' => 9,
'name' => 'Test Banan',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.15,
'depth' => 3,
),
),
),
1 =>
array (
'id' => 10,
'name' => 'Test Sesam Seeds',
'metric_id' => 1,
'category_id' => NULL,
'quantity' => 0.1,
'depth' => 2,
),
),
),
)
Как я могу сократить такую структуру до списка количества/веса каждого отдельного «конечного» ингредиента, такого как мука, вода, бананы, сыр, ветчина, семена кунжута и т. д.?
Я написал эту функцию для запуска структуры, но на самом деле она не выполняет свою работу (похоже, она правильно вычисляет общий вес).
function resolveAssembly($items, $number = 1, $declaration = [])
{
$weight = 0;
foreach ($items as $item) {
// Get the quantity (weight) of the item
$amount = $item['quantity'];
// Check if the item also contains an ingredients list
if (array_key_exists('parts', $item)) {
// Get the total weight of the whole assembly
list($assembly_weight, $assembly_declaration) = resolveAssembly($item['parts'], $number * $amount, $declaration);
$declaration = array_merge($declaration, $assembly_declaration);
// Calculate the relative weight of each part in the assembly
foreach ($item['parts'] as $part) {
$weight = $weight + ($amount / $assembly_weight) * $part['quantity'];
}
} else {
// Add the amount to the declaration
if (!array_key_exists($item['name'], $declaration)) {
$declaration[$item['name']] = $number * $amount;
}
$weight = $weight + $amount;
}
}
return [$weight, $declaration];
}
Обновлено: объединение массива объявлений сборки
Вот как я использую вышеуказанную функцию:
$item = Inventory::findOrFail(1); // Get "Test sandwich"
$parts = $item->getAssemblyItemsList(); // Get the parts of the "Test sandwich"
$result = resolveAssembly($parts, 1); // Get total weight of 1 "Test sandwich" + a list of amount/weight of each ingredient
Текущий результат функции resolveAssembly():
0.095
[
"Test Cheese" => 0.015
"Test Ham" => 0.03
"Test Flour" => 0.025
"Test Water" => 0.015
"Test Yeast" => 0.0025
"Test Banan" => 0.0075
"Test Sesam Seeds" => 0.005 // Should have been factored in the bread ingredients
]
Но это не совсем правильно, в сумме должно получиться 0,095, поэтому я думаю, что Тест Семена кунжута не учитывает состав хлеба (мука, дрожжи, банан и т. д.).
0,015+0,03+0,025+0,015+0,0025+0,0075+0,005 = 0,1 кг "Тестового бутерброда"
Также, если это поможет, я использую эту систему для хранения этих рецептов: https://github.com/stevebauman/inventory/blob/v1.7/docs/ASSEMBLIES.md (но изменен для хранения десятичных чисел, а также отрицательных чисел (например, для обработки потерянной или добавленной воды в процессе приготовления))
А вот и все расчеты, проведенные для суммирования общего веса «Тестового бутерброда»:
"Test Cheese: 0 + 0.015 = 0.015"
"Test Ham: 0.015 + 0.03 = 0.045"
"Test Flour: 0 + 0.5 = 0.5"
"Test Water: 0.5 + 0.3 = 0.8"
"Test Yeast: 0.8 + 0.05 = 0.85"
"Test Banan: 0.85 + 0.15 = 1"
"Test Flour: 0 + (1 / 1) * 0.5 = 0.5"
"Test Water: 0.5 + (1 / 1) * 0.3 = 0.8"
"Test Yeast: 0.8 + (1 / 1) * 0.05 = 0.85"
"Test Banan: 0.85 + (1 / 1) * 0.15 = 1"
"Test Sesam Seeds: 1 + 0.1 = 1.1"
"Test Bread: 0.045 + (0.05 / 1.1) * 1 = 0.09045454545"
"Test Sesam Seeds: 0.090454545454545 + (0.05 / 1.1) * 0.1 = 0.095"
Всем было бы намного проще, если бы этот массив был принтером с var_export вместо этого.
RiggsFolly - вы правы, я забыл включить свою функцию - она вроде правильно вычисляет общий вес. Андреас - исправлено (спасибо, что указали)
«Тестовый хлеб с кунжутом» состоит из «Тестового хлеба» и «Тестовых семян кунжута», должны ли все три из них (и любые их части) быть указаны в списке ингредиентов? Поскольку используется только часть «Тестового хлеба с кунжутом», нужно ли это учитывать в его подразделах?
@inckie, если бы вы могли опубликовать, как вы ожидаете, что результат будет выглядеть, это было бы полезно
@khartnett - добавил результат, но значения неверны, но мне нужно количество каждого «конечного» ингредиента, используемого для приготовления одного рецепта «Тестового сэндвича». Итак, мне нужно количество сырной ветчины, муки, воды, семян и т. д.






Это должно сблизить вас:
function resolveAssembly($items) {
$weight = 0;
$declaration = [];
foreach ($items as $item) {
$weight += $item['quantity'];
if (array_key_exists('parts', $item)) {
// if there are parts, get the full weight and declaration for the sub item
list($sub_total_weight, $sub_declaration) = resolveAssembly($item['parts']);
foreach ($sub_declaration as $sub_name => $sub_weight) {
if (!array_key_exists($sub_name, $declaration)) {
$declaration[$sub_name] = 0;
}
// Total weight of particular ingredient is the sub items ingredient out of the sub items full weight
// times the quantity for this particular item
$declaration[$sub_name] += $item['quantity'] * ($sub_weight / $sub_total_weight);
}
} else {
// if there are no parts, just add the quantity
if (!array_key_exists($item['name'], $declaration)) {
$declaration[$item['name']] = 0;
}
$declaration[$item['name']] += $item['quantity'];
}
}
return [$weight, $declaration];
}
Это даст вам следующий результат:
array(2) {
[0]=>
float(0.095)
[1]=>
array(7) {
["Test Cheese"]=>
float(0.015)
["Test Ham"]=>
float(0.03)
["Test Flour"]=>
float(0.022727272727273)
["Test Water"]=>
float(0.013636363636364)
["Test Yeast"]=>
float(0.0022727272727273)
["Test Banan"]=>
float(0.0068181818181818)
["Test Sesam Seeds"]=>
float(0.0045454545454545)
}
}
Если сложить приведенные выше числа, они должны составить 0,095.
По сути, когда вы делаете «суб-рецепт», вам нужно знать, каков общий «вес» этого рецепта, и оттуда вы можете вычислить фактическое общее количество предметов на основе количества этого суб-рецепта.
Куда они должны добавить это? Что оно делает? Предоставьте некоторый контекст для вашего ответа.
Правильно ли его объединить? Потому что, как вы сказали, что если ингредиент в декларации сборки уже существует в декларации, то его нужно добавить (я думаю)
Кажется, есть смесь того, что такое «количество» и «вес». Например, он требует 0,05 "тестового хлеба с кунжутом", однако две части "тестового хлеба с кунжутом" в сумме составляют 1,1 кг.
И да и нет. Я думаю об этом так: вы не печете одну буханку хлеба, вы выпекаете полный хлеб весом 1 кг и добавляете в него 0,1 кг (100 г) семян кунжута, чтобы создать еще один вариант хлеба. Затем вы отрезаете буханку этого хлеба весом 0,05 кг (50 г), чтобы приготовить сэндвич.
Большое вам спасибо - я проверил функцию на реальных данных рецепта, и она возвращает тот же результат, что и старая программа Delphi ~ 25-летней давности, которая в настоящее время используется для выполнения этих вычислений.
Рад слышать! Мой метод состоял в том, чтобы начать проще с рецепта, состоящего только из ингредиентов, выполняя $result = resolveAssembly($parts[2]['parts'][0]['parts']);. Как только это сработало, я добавил детали и перешел на уровень $result = resolveAssembly($parts[2]['parts']); и проверил результаты. Только после этого заработало.
Ваша помощь была действительно полезной, но теперь она нужна мне для другого дела. Хотите посмотреть stackoverflow.com/questions/56909578/… ?
Покажите нам, что вы хотя бы пытались решить эту проблему самостоятельно, показав код, который у вас есть.