Проблема в том, что каждый дочерний элемент привязан к каждому родительскому элементу, поэтому все дублируется.
<?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 то есть есть элемент с уровнем = 1 (он родитель), цикл идет дальше если элемент с уровнем = 2, то он дочерний элемент предыдущего элемента и только его Если следующий элемент с уровнем = 1, то у него уже будут другие дети
Вы всегда перебираете все данные. Вы можете пометить использованные, чтобы больше не использовать их.
вы хотите, чтобы он анализировался по порядку, все уровни 1 являются братьями и сестрами, а в противном случае каждый элемент является дочерним элементом предыдущего?
@ITgoldman Все 1 уровень - родители, а все остальные - дети и дети детей (тогда есть еще уровень = 3)






Если вы хотите, чтобы он анализировался по порядку, все уровни 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)да ваш вариант реально работает, сейчас буду внимательно все читать
Я вижу две проблемы в вашем коде:
$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, не стесняйтесь удалять типы в сигнатуре функции, чтобы он работал со старыми версиями.
спасибо большое, получилось именно так как я и хотел Теперь осталось понять как это работает
@AlexBoyarchuk: главное сохранить: когда вы передаете массив в функцию, она использует копию этого массива в теле функции, поэтому нет никаких шансов сохранить позицию курсора/указателя при вызове функции. Другое дело, что каждый раз, когда вызывается цикл foreach, положение указателя массива также сбрасывается. Но когда итератор (или любой объект) передается функции, функция не делает копию и работает с самим объектом. Вот почему позиция указателя сохраняется между вызовами.
то есть грубо говоря, если использовать в цикле просто массив, не оборачивая его в итератор, то на каждой итерации указатель сбрасывается и мы никак не можем его задать?
@AlexBoyarchuk: не на каждой итерации, а при каждом запуске цикла foreach. Также каждый раз, когда вы вызываете функцию с массивом, поскольку это копия массива, по умолчанию она устанавливается на первую позицию. Экземпляр итератора отличается тем, что он содержит в себе последнюю позицию (это свойство). И когда вы передаете экземпляр объекта функции, вы передаете ссылку на экземпляр, а не копию. Поэтому последняя позиция сохраняется.
это интересно, я с таким не сталкивался, вы очень хороший специалист, спасибо большое. Не подскажете, есть ли у вас канал на ютубе или как с вами связаться, буду очень благодарен
@AlexBoyarchuk: Извините, единственный канал YouTube, который я могу предложить, это: youtube.com/watch?v=LRd2C09s0aQ
А чего вы хотите добиться? Каким должно быть желаемое дерево?