Иерархический массив PHP - родители и дети

Я использую PHP и MySQL с Idiorm. Это может быть не актуально.

Мой PHP массив

  • Это отношения между родителями и детьми.
  • 0 является корневым родителем.
  • Пример: Коренной родитель 0 имеет ребенка 33, у которого есть ребенок 27, у которого есть ребенок 71.

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

array (
  33 => 
    array (
      0 => '27',
      1 => '41',
  ),
  27 => 
    array (
      0 => '64',
      1 => '71',
  ),
  0 => 
    array (
      0 => '28',
      1 => '29',
      2 => '33',
  ),
)

Мой иерархический результат

Как то так, но как массив...

  0 => 
      28
      29
      33
         27 =>
               64
               71
         41

Информация

  • Глубина неизвестна, и она может быть неограниченной. Я пытался foreach, но это может быть не так.

Мои собственные мысли

  • Некоторая рекурсивная функция?
  • Некоторые циклы пока?

Я попробовал оба вышеперечисленных, только что получил беспорядок. Это ежу понятно.

4 ответа

Решение

Предложение @deceze сработало. Однако входной массив должен изменить лит, как это...

$rows = array(
    array(
        'id' => 33,
        'parent_id' => 0,
    ),
    array(
        'id' => 34,
        'parent_id' => 0,
    ),
    array(
        'id' => 27,
        'parent_id' => 33,
    ),
    array(
        'id' => 17,
        'parent_id' => 27,
    ),
);

С /questions/46441321/rekursivnaya-funktsiya-dlya-generatsii-mnogomernogo-massiva-iz-rezultata-bazyi-dannyih/46441356#46441356:

function buildTree(array $elements, $parentId = 0) {
    $branch = array();

    foreach ($elements as $element) {
        if ($element['parent_id'] == $parentId) {
            $children = buildTree($elements, $element['id']);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[] = $element;
        }
    }

    return $branch;
}

$tree = buildTree($rows);

print_r( $tree );

Я добавил к ответам @Jens Törnell, чтобы включить определение параметров для имени столбца parent_id, имени ключа дочернего массива, а также имени столбца для id.

/**
 * function buildTree
 * @param array $elements
 * @param array $options['parent_id_column_name', 'children_key_name', 'id_column_name'] 
 * @param int $parentId
 * @return array
 */
function buildTree(array $elements, $options = [
    'parent_id_column_name' => 'parent_id',
    'children_key_name' => 'children',
    'id_column_name' => 'id'], $parentId = 0)
    {
    $branch = array();
    foreach ($elements as $element) {
        if ($element[$options['parent_id_column_name']] == $parentId) {
            $children = buildTree($elements, $options, $element[$options['id_column_name']]);
            if ($children) {
                $element[$options['children_key_name']] = $children;
            }
            $branch[] = $element;
        }
    }
    return $branch;
}

Поскольку функциональность довольно универсальна, мне удалось использовать вышеуказанную функцию в большинстве моих проектов.

Отличный ответ от @Jens Törnell, просто хотел добавить небольшое улучшение: если ваш parent_id и id на самом деле строка, а не число, то приведенный выше метод завершится неудачно, и после создания дочернего массива он снова создаст эти дочерние массивы как отдельный отдельный массив. Чтобы это исправить, вы должны выполнить тройную проверку на равенство и указать тип данных переменной ie (string) в сравнении.

Для строкового идентификатора и Parent_id в массиве

function buildTree(array $elements, $parentId = 0) {
    $branch = array();

    foreach ($elements as $element) {
        if ((string)$element['parent_id']  === (string)$parentId) {
            $children = buildTree($elements, $element['id']);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[] = $element;
        }
    }

    return $branch;
}

Кроме того, если кто-то хочет, он может добавить третий параметр в функцию, а также динамически указывать тип данных переменных, т.е. function buildTree(array $elements, $parentId = 0, $datatype='string') но тогда вам придется принять любую другую ошибку.

надеюсь, это поможет кому-то!

public function createTree (&$list, $parentId = null) {
    $tree = array();
    foreach ($list as $key => $eachNode) {
        if ($eachNode['parentId'] == $parentId) {
            $eachNode['children'] = $this->createTree ($list,$eachNode['id']);
            $tree[] = $eachNode;
            unset($list[$key]);
        }
    }
    return $tree;
}

В этой функции передайте ассоциативный массив, и если самый родительский элемент не равен нулю, просто передайте самый родительский идентификатор как второй аргумент.

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

Если кому-то еще нужно построить дерево без корневого идентификатора родительского элемента, вот как я это сделал.

<?php

$rows = [
    (object) [
        'id' => 1001,
        'parentid' => 1000,
        'name' => 'test1.1'
    ],
    (object) [
        'id' => 1000,
        'parentid' => 100,
        'name' => 'test1'
    ],
    (object) [
        'id' => 1002,
        'parentid' => 1000,
        'name' => 'test1.2'
    ],
    (object) [
        'id' => 1004,
        'parentid' => 1001,
        'name' => 'test1.1.1'
    ],
    (object) [
        'id' => 1005,
        'parentid' => 1004,
        'name' => 'test1.1.1.1'
    ],
    (object) [
        'id' => 100,
        'parentid' => 10,
        'name' => 'test 0'
    ],
    (object) [
        'id' => 1006,
        'parentid' => 1002,
        'name' => 'test1.2.1'
    ],
    (object) [
        'id' => 1007,
        'parentid' => 1002,
        'name' => 'test1.2.2'
    ],
];

function add_child(stdClass $parent, stdClass $child) {
    if ($child->parentid != $parent->id) {
        throw new Exception('Attempting to add child to wrong parent');
    }

    if (empty($parent->children)) {
        $parent->children = [];
    } else {
        // Deal where already in branch.
        foreach ($parent->children as $idx => $chd) {
            if ($chd->id === $child->id) {
                if (empty($chd->children)) {
                    // Go with $child, since $chd has no children.
                    $parent->children[$idx] = $child;
                    return;
                } else {
                    if (empty($child->children)) {
                        // Already has this child with children.
                        // Nothing to do.
                        return;
                    } else {
                        // Both childs have children - merge them.
                        $chd->children += $child->children;
                        $parent->children[$idx] = $child;
                        return;
                    }
                }
            }
        }
    }

    $parent->children[] = $child;
}

function build_branch(&$branch, &$rows, &$parent = null) {
    $hitbottom = false;
    while (!$hitbottom) {
        $foundsomething = false;
        // Pass 1 - find children.
        $removals = []; // Indexes of rows to remove after this loop.
        foreach ($rows as $idx => $row) {
            if ($row->parentid === $branch->id) {
                // Found a child.
                $foundsomething = true;
                // Recurse - find children of this child.
                build_branch($row, $rows, $branch);
                add_child($branch, $row);
                $removals[] = $idx;
            }
        }
        foreach ($removals as $idx) {
            unset($rows[$idx]);
        }
        // Pass 2 - find parents.
        if ($parent === null) {
            $foundparent = false;
            foreach ($rows as $idx => $row) {
                if ($row->id === $branch->parentid) {
                    // Found parent
                    $foundsomething = true;
                    $foundparent = true;

                    add_child($row, $branch);
                    unset ($rows[$idx]);
                    // Now the branch needs to become the parent since parent contains branch.
                    $branch = $row;
                    // No need to search for other parents of this branch.
                    break;
                }
            }
        }

        $hitbottom = !$foundsomething;
    }
}

function build_tree(array $rows) {
    $tree = [];
    while (!empty($rows)) {
        $row = array_shift($rows);
        build_branch($row, $rows);
        $tree[] = $row;
    }
    return $tree;
}

$tree = build_tree($rows);

print_r($tree);
Другие вопросы по тегам