Создание дерева из плоского массива в PHP
Я просмотрел интернет и не совсем нашел то, что искал. У меня есть плоский массив с каждым элементом, содержащим "id" и "parent_id". Каждый элемент будет иметь только один родительский элемент, но может иметь несколько дочерних элементов. Если parent_id = 0, он считается элементом уровня корня. Я пытаюсь получить свой плоский массив в дерево. Другие образцы, которые я нашел, только копируют элемент в родительский, но оригинал все еще существует.
ИЗМЕНИТЬ
Каждый элемент стартового массива считывается из отдельного файла XML. Сам файл будет иметь значение "0" в качестве значения parent_id, если у него нет родителя. Клавиши на самом деле являются строками.
Прошу прощения за путаницу раньше. Надеюсь, это более понятно:
/EDIT
Мой начальный массив:
Array
(
[_319_] => Array
(
[id] => 0
[parent_id] => 0
)
[_320_] => Array
(
[id] => _320_
[parent_id] => 0
)
[_321_] => Array
(
[id] => _321_
[parent_id] => _320_
)
[_322_] => Array
(
[id] => _322_
[parent_id] => _321_
)
[_323_] => Array
(
[id] => _323_
[parent_id] => 0
)
[_324_] => Array
(
[id] => _324_
[parent_id] => _323_
)
[_325_] => Array
(
[id] => _325_
[parent_id] => _320_
)
)
Результирующий массив после дерева:
Array
(
[_319_] => Array
(
[id] => _319_
[parent_id] => 0
)
[_320_] => Array
(
[id] => _320_
[parent_id] => 0
[children] => Array
(
[_321_] => Array
(
[id] => _321_
[parent_id] => _320_
[children] => Array
(
[_322_] => Array
(
[id] => _322_
[parent_id] => _321_
)
)
)
[_325_] => Array
(
[id] => _325_
[parent_id] => _320_
)
)
[_323_] => Array
(
[id] => _323_
[parent_id] => 0
[children] => Array
(
[_324_] => Array
(
[id] => _324_
[parent_id] => _323_
)
)
)
Любая помощь/руководство приветствуются!
Некоторый код, который у меня есть до сих пор:
function buildTree(array &$elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = $this->buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
Ответы
Ответ 1
Ты забыл unset()
там, где есть.
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['id']] = $element;
unset($elements[$element['id']]);
}
}
return $branch;
}
Ответ 2
Решение ImmortalFirefly работает, однако, как указывает mrded, оно не спасает первых родителей без детей. Я отредактировал эту функцию, чтобы исправить эту проблему:
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['id']] = $element;
unset($element);
}
}
return $branch;
}
Ответ 3
Это работает для меня:
$index=array();
$tree=array();
foreach ($ori as $key=>$var) {
$var=array_shift($ori);
if ($var['id']==0) $var['id']=$key;
if ((string)$var['parent_id']==='0') {
$tree[$key]=$var;
$index[$key]=&$tree[$key];
} else if (isset($index[$var['parent_id']])) {
if (!isset($index[$var['parent_id']]['children'])) $index[$var['parent_id']]['children']=array();
$index[$var['parent_id']]['children'][$key]=$var;
$index[$key]=&$index[$var['parent_id']]['children'][$key];
} else {
array_push($ori,$var);
}
}
unset($index);
print_r($tree);
Ответ 4
Я вижу логику, за исключением этого в результате:
Array
(
[0] => Array
(
[id] => 0
[parent_id] => 0
)
[1] => Array
(
[id] => 1
[parent_id] => 0
)
IMHO, parent_id = o, не должен [1] быть дочерним элементом [0] здесь?
В любом случае, ссылки на спасение:
$tree = array();
foreach($inputarray as $item){
if(!isset($tree[$item['id']])) $tree[$item['id']] = array();
$tree[$item['id']] = array_merge($tree[$item['id']],$item);
if(!isset($tree[$item['parent_id']])) $tree[$item['parent_id']] = array();
if(!isset($tree[$item['parent_id']]['children'])) $tree[$item['parent_id']]['children'] = array();
$tree[$item['parent_id']]['children'][] = &$tree[$item['id']];
}
$result = $tree[0]['children'];
unset($tree);
print_r($result);
Поскольку вы злоупотребляли 0 как "магическое" число как root, так и существующий id, теперь мы имеем рекурсию в ветке id = 0. Добавление if($item['parent_id']!=$item['id'])
до $tree[$item['parent_id']]['children'][] = &$tree[$item['id']];
может помешать этому, но это не так.
Ответ 5
Возможно, что исходный массив немного отличается, вы можете использовать эту функцию (parent_id, id, title):
$q = mysql_query("SELECT id, parent_id, name FROM categories");
while ($r = mysql_fetch_row($q)) {
$names[$r[0]] = $r[2];
$children[$r[0]][] = $r[1];
}
function render_select($root=0, $level=-1) {
global $names, $children;
if ($root != 0)
echo '<option>' . strrep(' ', $level) . $names[$root] . '</option>';
foreach ($children[$root] as $child)
render_select($child, $level+1);
}
echo '<select>';
render_select();
echo '</select>';
Ответ 6
Хотя это старый вопрос, я собираюсь опубликовать свой ответ здесь:
/* assuming top level pid = 0 */
$rows = array (
array ( 'id' => 1, 'pid' => 0 ),
/* ... */
);
/* make id become array key */
$rows = array_column ( $rows, null, 'id' );
foreach ( $rows as $key => $val ) {
if ( $val ['pid'] ) {
if ( isset ( $rows [$val ['pid']] )) {
$rows [$val ['pid']]['children'][] = &$rows [$key];
}
}
}
foreach ( $rows as $key => $val ) {
if ( $val ['pid'] ) unset ( $rows [$key] );
}
array_column
- это PHP 5.5, но вы можете сделать свой собственный легко.
Ответ 7
Вы хотите смотреть на хранение и загрузку иерархических данных в MySQL, поскольку я должен решить несколько проблем. Я предполагаю, что первый массив представляет данные, полученные непосредственно из базы данных?
Похоже, вы пытаетесь использовать модель смежности для организации своих данных в структуре иерархии. Есть и другие способы достижения этой цели с помощью гнездования. Если вы не берете эти данные из базы данных, это может быть не так полезно.
Эта ссылка должна помочь вам: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
Ответ 8
Код SteveEdson работает нормально, за исключением случая, когда родительский элемент не существует в исходной структуре данных. Вот мое исправление для этого (однако, оно удаляет "parent_id" из элементов, которые могут или не могут быть приемлемы):
function buildTree(array &$elements, $parentId = 0)
{
$branch = array();
foreach ($elements as &$element) {
if ($element["parent_id"] != null && $elements[$element["parent_id"]] == null)
unset($element["parent_id"]);
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[$element['id']] = $element;
unset($element);
}
}
return $branch;
}
Ответ 9
Вот мое решение, идеально работает, если предположить, что верхний уровень parent_id = 0:
function MakeTree($arr){
$parents_arr=array();
foreach ($arr as $key => $value) {
$parents_arr[$value['pid']][$value['id']]=$value;
}
$tree=$parents_arr['0'];
$this->createTree($tree, $parents_arr);
return $tree;
}
function createTree(&$tree, $parents_arr){
foreach ($tree as $key => $value) {
if(!isset($value['children'])) {
$tree[$key]['children']=array();
}
if(array_key_exists($key, $parents_arr)){
$tree[$key]['children']=$parents_arr[$key];
$this->createTree($tree[$key]['children'], $parents_arr);
}
}
}
Ответ 10
Это мое решение, копирование и оптимизация других решений.
function buildTree(array &$elements, $parentId = 0) {
$branch = array();
foreach ($elements as $key => $element) {
if ($element['parent_id'] == $parentId) {
$children = $this->buildTree($elements, $key);
if ($children) {
$element['children'] = $children;
}
$branch[$key] = $element;
unset($elements[$key]);
}
}
return $branch;
}
Ответ 11
Чистое, короткое и свободное от балласта. Массив массивов к дереву:
class Mother {
private $root;
public function treeInit($array)
{
$this->root = new Child();
foreach($array as $value){
$this->root->treeClimb(array_reverse($value));
}
return $this->root;
}
}
class Child {
private $children = [];
public function treeClimb($arr)
{
if(count($arr) > 0) {
$childTmp = array_pop($arr);
if(!key_exists($childTmp,$this->children))
{
$this->children[$childTmp] = new Child();
}
$this->children[$childTmp]->treeClimb($arr);
}
}
}
$array = array(array('obst','banae','krumm','gelb'),
array('obst','beere','him'),
array('obst','beere','brom'),
array('obst','banae','gerade'),
array('veg','carot','gerade'));
$obj = new Mother();
var_dump($obj->treeInit($array));
Ответ 12
Я придумал решение, похожее на @eugen-rieck, и хотел поделиться им. Но я назвал $branches
своим массивом индексов.
$tree = [];
$branches = [];
while (!empty($input)) {
$beforeCount = count($input);
foreach ($input as $id => $item) {
$pid = $item['parent_id'];
if (isset($branches[$pid])) {
$branches[$pid]['children'][$id] = $item;
$branches[$id] = &$branches[$pid]['children'][$id];
unset($input[$id]);
}
}
if ($beforeCount === count($input)) {
$firstItem = array_shift($input);
$id = $firstItem['id'];
$tree[$id] = $firstItem;
$branches[$id] = &$tree[$id];
}
}
Ответ 13
Прошло много времени с момента вопроса, но я хочу поделиться своей функцией, поскольку я считаю, что она супер хороша для производительности, намного лучше, чем рекурсивная, даже больше для памяти, и это более понятно, чем другие подходы:
function buildTree(array &$elements, int $parentId = 0){
$categories = array();
$pointers = array();
foreach($elements as $key => $val)
{
$elements[$key]['id'] = $key;//Added due to author example
$category = intval($key);//Using $key due to author first id being 0 while key is 319
$parent = intval($val['parent_id']) === 0 ? $category : intval($val['parent_id']);
if(isset($pointers[$parent]))
{
$pointers[$parent][$category] = $val;
$pointers[$category] =& $pointers[$parent][$category];
}else
{
$categories[$category] = $val;
$pointers[$category] =& $categories[$category];
}
}
return $categories;
}