PHP конвертировать массив в многомерный массив по родительскому идентификатору

0

У меня есть одномерный массив с stdclasses внутри, эти объекты имеют имя и атрибут "abh". теперь я хочу получить древовидный вид, такой как многомерный массив. Допустим, мой массив выглядит так:

$ar = array(
    (object)array("name" => "Test1","otherstuff"=>array(),"abh"=>""),
    (object)array("name" => "Test2","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test3","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test4","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test5","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test6","otherstuff"=>array(),"abh"=>"Test5"),
    (object)array("name" => "Test7","otherstuff"=>array(),"abh"=>"Test5"),
    (object)array("name" => "Test8","otherstuff"=>array(),"abh"=>array("Test5","Test7")),
    (object)array("name" => "Test9","otherstuff"=>array(),"abh"=>"Test8"),
    (object)array("name" => "Test10","otherstuff"=>array(),"abh"=>"Test6"),
    (object)array("name" => "Test11","otherstuff"=>array(),"abh"=>"Test9"),
);

имена данных из функций, вызываемых ранее, а не из базы данных. Результат выглядит примерно так:

Array
(
[0] => stdClass Object
    (
        [name] => Test1
        [otherstuff] => Array
            (
            )

        [abh] => 
    )

[1] => stdClass Object
    (
        [name] => Test2
        [otherstuff] => Array
            (
            )

        [abh] => Test1
    )
[...]
)

поэтому теперь я хочу, чтобы этот массив сортировался по атрибуту "abh". первый, где "abh" пуст, его корень, все остальные станут его дочерьми. поэтому функция должна добавить атрибут "childs" к основному IF, если у него есть дочерние элементы. Но теперь этот тип является особым, потому что "abh" может быть массивом и переходить к n-родителям. В моем примере выше Test8 должен быть дочерним элементом Test5 и Test7 (клон).

Я попытался справиться с этим с помощью рекурсивной функции и циклов в foreach. Там он проверяет abh элемента againts, который существует, но не работает. array_filter() doenst делает это, как я хочу.

поэтому, я хочу этот результат:

Array
(
    [Test1] => stdClass Object
        (
            [name] => Test1
            [otherstuff] => Array()
            [abh] => 
            [childs] => Array
                (
                    [Test2] => stdClass Object
                        [...] and so on
                )
        )
)

любые идеи об этом? Я также не знаю, сколько "уровней" будет иметь возвращенный массив, т.е. сколько входных объектов и "abh" -assignes существует. Секунды состоят в том, что массив "abh" может иметь до 10 "родителей".

Спасибо за любую помощь!


как запросить мой нерабочий прототип для этого "сортировщика",

function checkABH(&$return,&$ar,$deph) {

    // clone all with array-abh
    $count = count($ar);
    for($x=0;$x<$count;$x++) {#$ar as$key=>$val) {
        if(is_array($ar[$x]->abh)) {
            $clone = $ar[$x];
            foreach($ar[$x]->abh as$abh) {
                #echo "<br>$x @@ $abh";
                $clone->abh = $abh;
                $ar[]       = $clone;
                #echo '<pre>'.print_r($clone,true).'</pre>';
            }
            // delete array-abh-element
            unset($ar[$x]);
        }
    }

    echo '<pre>'.print_r($ar,true).'</pre>';
    echo '</div>';
    echo '<div style="float:left;width:auto;margin:0px 10px;"><h3>Result:</h3>';

    // pass to sorter
    checkABH_a($return,$ar,$deph);
}

function checkABH_a(&$return,&$ar,$deph) {

    $test_abhs = array();

    foreach($ar as$key=>$val) {

        $val->childs = array();

        if(isset($return[$deph])&&isset($return[$deph]->name)&&$val->abh==$return[$deph]->name) {
            $return[$deph]->childs[] = $val;
            unset($ar[$key]);
        } elseif($val->abh==$deph) {
            $return[$val->abh] = $val;
            unset($ar[$key]);
        } else {
            $test_abhs[] = $val->abh;
        }
    }

    if(count($test_abhs)>0) {
        $test_abhs = array_unique($test_abhs);
        #echo '<pre>'.print_r($test_abhs,true).'</pre>';
        foreach($test_abhs as$abh) {
            checkABH_a($return,$ar,$abh);
        }
    }
}

echo '<div style="float:left;width:260px;border-right:1px solid #cccccc;margin:0px 10px;"><h3>Input</h3>';
echo '<pre>'.print_r($ar,true).'</pre>';
echo '</div>';
echo '<div style="float:left;width:260px;border-right:1px solid #cccccc;margin:0px 10px;"><h3>Cloned:</h3>';
$return = array();
checkABH($return,$ar,"");
echo'<pre>'.print_r($return,true).'</pre>';
echo '</div>';
  • 0
    А ты пробовал ... что ?? Как задать хороший вопрос
  • 0
    Как я уже говорил выше, рекурсивная функция, которая передает массив самому себе, пока он не станет пустым - в нем он проверяет, существует ли родительский элемент, и помещает этого потомка в него - но мое решение не будет работать
Показать ещё 6 комментариев
Теги:
arrays
multidimensional-array

1 ответ

1
Лучший ответ

Эти рекурсивные деревья не становятся более легкими, что добавляет требование "множественного родителя";//Что бы это ни было, оно все еще выглядит интересным...

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

Заметки:

  • конвертировал (дублировал) "несколько родителей" в отдельные записи, поэтому функция "insertNode" была легче понятна.
  • вместо использования "stdClass" я создал класс под названием "TreeNode", поскольку я могу добавить методы позже.

Рабочая версия кода на Viper-7.com с использованием PHP 5.3.18

Любые вопросы о коде, а затем отправьте комментарии, и я попробую ответить им.

<?php // https://stackoverflow.com/questions/27360813/php-convert-array-to-multidimensional-array-by-parent-id

$ar = array(
    (object) array("name" => "Test1", "otherstuff"=>array('parent is Test1'), "abh"=>""),
    (object) array("name" => "Test2", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test3", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test4", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test5", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test6", "otherstuff"=>array('parent is Test5'), "abh"=>"Test5"),
    (object) array("name" => "Test7", "otherstuff"=>array('parent is Test5'), "abh"=>"Test5"),
    (object) array("name" => "Test8", "otherstuff"=>array('parent is Test5 AND Test7'), "abh"=>array("Test5", "Test7")),
    (object) array("name" => "Test9", "otherstuff"=>array('parent is Test8'), "abh"=>"Test8"),
    (object) array("name" => "Test10", "otherstuff"=>array('parent is Test6'), "abh"=>"Test6"),
    (object) array("name" => "Test11", "otherstuff"=>array('parent is Test9'), "abh"=>"Test9"),
);


/*
 * The requirement is that for any child then the parent must be in the tree already.
 */

// Convert those parent with 'arrays of parents' into individual entries as it will make the insert
// easier to understand.

// i will also convert all the input into a 'TreeNode' class while i am doing this pass down the input. 
// i can add some methods later if i wish.

// i will also get the root of the tree while i am doing this...
$rootNode = new TreeNode(array_shift($ar));

$treeNodeList = array();
foreach($ar as $node) {
    if (is_array($node->abh)) { // generate duplicate nodes
        foreach($node->abh as $parentName) {
            $node->abh = $parentName; // make it an ordinary node
            $treeNodeList[] = new TreeNode($node);
        }
        continue;
    }
    $treeNodeList[] = new TreeNode($node);
}

// var_dump($ar, $rootNode, $treeNodeList);

// Ok, we now have a node list in the appropriate order - let us build the tree

/**
 * This is a 'multiway' tree .i.e. there can be any number of 'child' nodes
 *                                 for any parent
 * Usual rules:
 *   o The parent MUST be in the tree already.
 *   o Children are added, by searching from the root of the tree, in this version
 *
 */

// the output will be here
$theTree = array($rootNode->name => $rootNode);

foreach ($treeNodeList as $childNode) { // add it
    $inserted = insertNode($rootNode, $childNode);
    if (!$inserted) {
      var_dump('Unable to insert:', $childNode, ' in tree:', $rootNode);
      die('i am here: '. __FILE__.__LINE__);
    }
}

// show the tree
echo '<pre>';
   print_r($theTree);
echo '</pre>';

exit;
// --------- end of processing -------------

/**
 * Insert the node in the tree
 *
 * @param TreeNode $parent
 * @param TreeNode $newChild
 */
function insertNode(TreeNode &$parent, TreeNode $newChild) {
    if ($newChild->abh === $parent->name) { // add it to the current parent
        $parent->children[$newChild->name] = $newChild; // add child
        return true;
    }

    // check the children
    foreach($parent->children as $newParent) {
       if (insertNode($newParent, $newChild)) {
          return true;
       }
    }
    return false; // unable to insert child in the tree
}

// -----------------------------------------------------------------------
/**
 * treeNode: (Data and Relationships)
 *   nodeId:            'name'
 *   nodeData:          'otherstuff'
 *   nodeParentList:    'abh' - may be a list of parents.
 *                          Note: In this version of the code: All the parents
 *                                MUST be in the tree already.
 *   children:          array of treeNode - recursive structure
 */

// i will make the properties all public to make it easier to debug.

class TreeNode  {
    public $name = '';
    public $otherstuff = array();
    public $abh = '';
    public $children = array(); // list of child nodes

    public function __construct(stdClass $node)
    {
        $this->name = $node->name;
        $this->otherstuff = $node->otherstuff;
        $this->abh = $node->abh;
    }
} // class end --------------------------
  • 0
    Спасибо вам большое! именно то, что я хотел

Ещё вопросы

Сообщество Overcoder
Наверх
Меню