Как рассчитать количество каждого ингредиента?

Я пытаюсь решить проблему, где мне нужно рассчитать пищевую ценность рецепта, такого как «Мой бутерброд».

У меня есть база данных с «рецептами» и «ингредиентами» и необходимым количеством в кг.

рецепт может содержать ингредиенты, но также и другие рецепты, которые также могут содержать рецепты/ингредиенты.

Это пример «списка деталей» «Тестового бутерброда» общим весом 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"

Покажите нам, что вы хотя бы пытались решить эту проблему самостоятельно, показав код, который у вас есть.

RiggsFolly 01.05.2019 17:49

Всем было бы намного проще, если бы этот массив был принтером с var_export вместо этого.

Andreas 01.05.2019 17:52

RiggsFolly - вы правы, я забыл включить свою функцию - она ​​вроде правильно вычисляет общий вес. Андреас - исправлено (спасибо, что указали)

inckie 01.05.2019 18:12

«Тестовый хлеб с кунжутом» состоит из «Тестового хлеба» и «Тестовых семян кунжута», должны ли все три из них (и любые их части) быть указаны в списке ингредиентов? Поскольку используется только часть «Тестового хлеба с кунжутом», нужно ли это учитывать в его подразделах?

khartnett 01.05.2019 20:38

@inckie, если бы вы могли опубликовать, как вы ожидаете, что результат будет выглядеть, это было бы полезно

khartnett 01.05.2019 20:47

@khartnett - добавил результат, но значения неверны, но мне нужно количество каждого «конечного» ингредиента, используемого для приготовления одного рецепта «Тестового сэндвича». Итак, мне нужно количество сырной ветчины, муки, воды, семян и т. д.

inckie 01.05.2019 23:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
6
852
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это должно сблизить вас:

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.

По сути, когда вы делаете «суб-рецепт», вам нужно знать, каков общий «вес» этого рецепта, и оттуда вы можете вычислить фактическое общее количество предметов на основе количества этого суб-рецепта.

Куда они должны добавить это? Что оно делает? Предоставьте некоторый контекст для вашего ответа.

miken32 01.05.2019 21:27

Правильно ли его объединить? Потому что, как вы сказали, что если ингредиент в декларации сборки уже существует в декларации, то его нужно добавить (я думаю)

inckie 02.05.2019 00:00

Кажется, есть смесь того, что такое «количество» и «вес». Например, он требует 0,05 "тестового хлеба с кунжутом", однако две части "тестового хлеба с кунжутом" в сумме составляют 1,1 кг.

khartnett 02.05.2019 01:10

И да и нет. Я думаю об этом так: вы не печете одну буханку хлеба, вы выпекаете полный хлеб весом 1 кг и добавляете в него 0,1 кг (100 г) семян кунжута, чтобы создать еще один вариант хлеба. Затем вы отрезаете буханку этого хлеба весом 0,05 кг (50 г), чтобы приготовить сэндвич.

inckie 02.05.2019 01:20

Большое вам спасибо - я проверил функцию на реальных данных рецепта, и она возвращает тот же результат, что и старая программа Delphi ~ 25-летней давности, которая в настоящее время используется для выполнения этих вычислений.

inckie 02.05.2019 16:43

Рад слышать! Мой метод состоял в том, чтобы начать проще с рецепта, состоящего только из ингредиентов, выполняя $result = resolveAssembly($parts[2]['parts'][0]['parts']);. Как только это сработало, я добавил детали и перешел на уровень $result = resolveAssembly($parts[2]['parts']); и проверил результаты. Только после этого заработало.

khartnett 02.05.2019 18:25

Ваша помощь была действительно полезной, но теперь она нужна мне для другого дела. Хотите посмотреть stackoverflow.com/questions/56909578/… ?

inckie 05.07.2019 23:56

Другие вопросы по теме