JSON Tree PHP с использованием рекурсивной функции

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

<?php
$json = '[
    {"id":3633,"name":"Mobile phones and accessories","left":1,"right":18,"level":1,"elements":0},
    {"id":3638,"name":"Mobile phones","left":2,"right":3,"level":2,"elements":174},
    
    {"id":21396,"name":"Tablets","left":19,"right":24,"level":1,"elements":0},
    {"id":21450,"name":"Some Tablets","left":20,"right":21,"level":2,"elements":8}
    ]';

$data = json_decode($json, true);

function buildTree($data, $level = 1) {
    $tree = array();

    foreach ($data as $item) {
        if ($item['level'] == $level) {
            $node = array(
                'item' => $item,
                'children' => buildTree($data, $level + 1),
            );

            $tree[] = $node;
        }
    }

    return $tree;
}

$tree = buildTree($data);

print_r($tree);

?>

Получается, что это выход элементов

Array
(
    [0] => Array
        (
            [item] => Array
                (
                    [id] => 3633
                    [name] => Mobile phones and accessories
                    [left] => 1
                    [right] => 18
                    [level] => 1
                    [elements] => 0
                )

            [children] => Array
                (
                    [0] => Array
                        (
                            [item] => Array
                                (
                                    [id] => 3638
                                    [name] => Mobile phones
                                    [left] => 2
                                    [right] => 3
                                    [level] => 2
                                    [elements] => 174
                                )

                            [children] => Array
                                (
                                )

                        )

                    [1] => Array
                        (
                            [item] => Array
                                (
                                    [id] => 21450
                                    [name] => Some Tablets
                                    [left] => 20
                                    [right] => 21
                                    [level] => 2
                                    [elements] => 8
                                )

                            [children] => Array
                                (
                                )

                        )

                )

        )

    [1] => Array
        (
            [item] => Array
                (
                    [id] => 21396
                    [name] => Tablets
                    [left] => 19
                    [right] => 24
                    [level] => 1
                    [elements] => 0
                )

            [children] => Array
                (
                    [0] => Array
                        (
                            [item] => Array
                                (
                                    [id] => 3638
                                    [name] => Mobile phones
                                    [left] => 2
                                    [right] => 3
                                    [level] => 2
                                    [elements] => 174
                                )

                            [children] => Array
                                (
                                )

                        )

                    [1] => Array
                        (
                            [item] => Array
                                (
                                    [id] => 21450
                                    [name] => Some Tablets
                                    [left] => 20
                                    [right] => 21
                                    [level] => 2
                                    [elements] => 8
                                )

                            [children] => Array
                                (
                                )

                        )

                )

        )

)

Теперь у меня каждый элемент с уровнем = 2 привязан к каждому элементу с уровнем = 1. Но дублирования быть не должно.

Что можно изменить, чтобы все элементы были уникальными и принадлежали только одному родителю

А чего вы хотите добиться? Каким должно быть желаемое дерево?

digitalniweb 12.07.2023 17:12

@digitalniweb то есть есть элемент с уровнем = 1 (он родитель), цикл идет дальше если элемент с уровнем = 2, то он дочерний элемент предыдущего элемента и только его Если следующий элемент с уровнем = 1, то у него уже будут другие дети

Alex Boyarchuk 12.07.2023 17:16

Вы всегда перебираете все данные. Вы можете пометить использованные, чтобы больше не использовать их.

Markus Zeller 12.07.2023 17:23

вы хотите, чтобы он анализировался по порядку, все уровни 1 являются братьями и сестрами, а в противном случае каждый элемент является дочерним элементом предыдущего?

IT goldman 12.07.2023 17:26

@ITgoldman Все 1 уровень - родители, а все остальные - дети и дети детей (тогда есть еще уровень = 3)

Alex Boyarchuk 12.07.2023 17:33
Стоит ли изучать 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 и хотите разрабатывать...
1
5
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы хотите, чтобы он анализировался по порядку, все уровни 1 являются братьями и сестрами, а в противном случае каждый элемент является дочерним элементом предыдущего, тогда вот псевдокод javascript.

var arr = [
    {"id":3633,"name":"Mobile phones and accessories","left":1,"right":18,"level":1,"elements":0},
    {"id":3638,"name":"Mobile phones","left":2,"right":3,"level":2,"elements":174},
    {"id":3639,"name":"Mobile phones 2","left":2,"right":3,"level":3,"elements":174},
    
    {"id":21396,"name":"Tablets","left":19,"right":24,"level":1,"elements":0},
    {"id":21450,"name":"Some Tablets","left":20,"right":21,"level":2,"elements":8}
];

var result = [];

var previous = null;
arr.forEach(function(item) {
  if (item.level == 1) {
    result.push({item})
    previous = item;
  } else {
    previous.children = previous.children || []
    previous.children.push({item})
    previous = item;
  }
})

console.info(result)

да ваш вариант реально работает, сейчас буду внимательно все читать

Alex Boyarchuk 12.07.2023 19:48
Ответ принят как подходящий

Я вижу две проблемы в вашем коде:

  • он не обрабатывает случай, когда $level больше, чем $item['level']
  • рекурсивный вызов использует тот же массив, что и первый параметр, но это не сохраняет позицию «указателя», и весь массив сканируется при каждом вызове с самого начала.

Для первого пункта достаточно простого if, чтобы решить проблему. Для второго пункта я предлагаю использовать итератор вместо массива, чтобы сохранить позиции указателя через разные вызовы.

$data = new ArrayIterator(json_decode($json, true));

function buildTree(iterator $data, int $level = 1): array {
    
    $tree = [];
  
    while ($data->valid()) {
        $item = $data->current();
        
        if ($item['level'] < $level)
            return $tree;
  
        if ($item['level'] === $level) {
            $data->next();
            
            $tree[] = [
                'item' => $item,
                'children' => buildTree($data, $level + 1)
            ];
        }
    }

    return $tree;
}

демо

Этот код написан для PHP 8.x, не стесняйтесь удалять типы в сигнатуре функции, чтобы он работал со старыми версиями.

спасибо большое, получилось именно так как я и хотел Теперь осталось понять как это работает

Alex Boyarchuk 12.07.2023 19:49

@AlexBoyarchuk: главное сохранить: когда вы передаете массив в функцию, она использует копию этого массива в теле функции, поэтому нет никаких шансов сохранить позицию курсора/указателя при вызове функции. Другое дело, что каждый раз, когда вызывается цикл foreach, положение указателя массива также сбрасывается. Но когда итератор (или любой объект) передается функции, функция не делает копию и работает с самим объектом. Вот почему позиция указателя сохраняется между вызовами.

Casimir et Hippolyte 12.07.2023 20:23

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

Alex Boyarchuk 12.07.2023 21:06

@AlexBoyarchuk: не на каждой итерации, а при каждом запуске цикла foreach. Также каждый раз, когда вы вызываете функцию с массивом, поскольку это копия массива, по умолчанию она устанавливается на первую позицию. Экземпляр итератора отличается тем, что он содержит в себе последнюю позицию (это свойство). И когда вы передаете экземпляр объекта функции, вы передаете ссылку на экземпляр, а не копию. Поэтому последняя позиция сохраняется.

Casimir et Hippolyte 12.07.2023 21:13

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

Alex Boyarchuk 12.07.2023 21:19

@AlexBoyarchuk: Извините, единственный канал YouTube, который я могу предложить, это: youtube.com/watch?v=LRd2C09s0aQ

Casimir et Hippolyte 12.07.2023 21:47

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