简体   繁体   中英

Category hierarchy from array(cat id => parent id)

i am trying to create a multidimensional array hierarchy from a simple array which contains pairs of category ids and parent ids. The categories can be a parent and a subcategory at the same time. The base categories have a parent of 0 (=no parent). For example:

# cat_id => parent_id
$initialArray = array(
  1 => 0,
  2 => 1,
  3 => 2,

  4 => 0,
  5 => 4,

  6 => 0
);

From this, i'd like to get an array that represents a structure like this:

  • 1
    • 2
      • 3
  • 4
    • 5
  • 6

I will not know the contents of $initialArray beforehand.

I tried to look at other similar questions but i couldn't find an answer. Please help!

I know, late to the party but it looks interesting...

/*
 * This is a 'multiway' tree where:
 *   a 'parent' can have any number of 'child' nodes
 *   therefore the 'parent node' must look like:
 *   [parentId] => array()
 *
 *   where [parentId] is the index of an array;
 */

It will insert one node at a time starting from the root node. It can get very expensive for large trees.

Working example at: Viper-7.com

The routine that does the work:

/**
 * Insert:  Find the 'parent' node
 *          if child not found then insert a 'node'
 *
 * @param array node passed by reference as the new node will be inserted
 * @param integer $parentId - root Id must be the lowest value
 * @param integer $childId
 * @return boolean  true if parentId found and processed
 */
function insertNode(&$node, $parentId, $childId) {

    if (isset($node[$parentId])) { // this node will be processed
        if (!isset($node[$parentId][$childId])) {
            $node[$parentId][$childId] = array(); // add child node
            return true;
        }
        return true; // end of processing
    }

    // check all the children of this node...
    foreach($node as &$child) { // need the reference
        if (insertNode($child, $parentId, $childId)) {
            return true;
        }
    }
    return false; // parentId not in the tree
}

Notes:

The node to be processed is passed by reference.
The processing will end when the 'parent' id is found 

The input node list as given is 'child' => 'parent' order which is unusual, it is ok, just remember that in the processing...

Process the input data:

$theTree = array(current($links) => array()); // root

// insert all the child nodes into the tree
foreach($links as $childId => $parentId) {
    $inserted = insertNode($theTree, $parentId, $childId);
}

// output the tree
echo '<pre>', 'Children are in the same order as the input array.', '<br />';
    print_r($theTree);
echo '</pre>';

The input list needs to be arraged so that the 'tree' will be loaded such that the parent of the child to be added must be in the tree already. i assume that the input list is in the required order already

Test data, sort and display:

# cat_id => parent_id
$links = array(
   1 => 0,
   2 => 1,
   3 => 2,

   4 => 0,
   5 => 4, // multiple children
  11 => 4,
  99 => 4,
  13 => 11,

   6 => 0
);

Output, i added a sub-tree to the original input...

Children are in the same order as the input array.
Array
(
    [0] => Array
        (
            [1] => Array
                (
                    [2] => Array
                        (
                            [3] => Array
                                (
                                )
                        )
                )
            [4] => Array
                (
                    [5] => Array
                        (
                        )

                    [11] => Array
                        (
                            [13] => Array
                                (
                                )
                        )
                    [99] => Array
                        (
                        )
                )
            [6] => Array
                (
                )
        )
  )

Is there any way you can change the way that the array is generated? As you have it now, will be more hassle then it's worth to try and generate what you want.

Alternatively, you could try and generate something like this:

$menu = array(
    0 => array(0, 1, 2, 3),
    1 => array(0, 1),
);

Allowing a simple loop like this:

<ul>
    <?php foreach ($menu as $parent => $subs) : ?>
        <li>
            <?php echo $parent; ?>
            <ul>
                <?php foreach ($subs as $index => $val): ?>
                    <li>
                        <?php echo $val; ?>
                    </li>
                <?php endforeach; ?>
            </ul>
        </li>

    <?php endforeach; ?>
</ul>

The above is templating syntax, I'm sure we all know what this is so I'm now going to explain it.

Or the simple loop would look like this:

echo "<ul>";
foreach($menu as $parent => $subs){
    echo "<li>";
    echo $parent;
    echo "<ul>";
    foreach($subs as $index => $val) {
        echo "<li>";
        echo $val;
        echo "</li>";
    }
    echo "</ul>";
    echo "</li>";
}
echo "</ul>";

Get your list :

<?php 
$initialArray = array(
  1 => 0,
  2 => 1,
  3 => 2,
  4 => 0,
  5 => 4,
  6 => 0
);


$menus = array();

function generateMenus($start) {
    global $initialArray;
    foreach($initialArray as $k => $v) {
       if($start == $v) {
          $menus[] = $k;
       }
    }
    return $menus;
}
$final = array();
foreach($initialArray as $key => $value) {
   $final[$value] = generateMenus($value);
}

echo '<ul>';
function generateList($start) {
    global $final;
    echo '<li>'.$start.'</li>';
    if(!empty($final[$start])) {
        echo '<ul>';
        foreach($final[$start] as $v) {
            generateList($v);
        }
        echo '</ul>';
    }
}
generateList(0);
echo '</ul>';


?>

它将产生

Well it appears to me you need a recursive function. Assuming everything has a parent or value beginning at the base level of 0, I resituated the array to have all parent ids listing their children rather than the other way around above. After that, I created a recursive function.

$initialArray = array(
    1 => 0,
    2 => 1,
    3 => 2,

    4 => 0,
    5 => 4,

    6 => 0
);

// resituate the array
$parent_ids = array();
foreach ($initialArray as $category_id => $parent_id) {
    if (!isSet($parent_ids[$parent_id])) {
        $parent_ids[$parent_id] = array();
    }
    $parent_ids[$parent_id][] = $category_id;
}

// end_array is the result
$end_array = array();

/**
 * Takes the key of the parent, the current set that it's working off of, the list of parent ids for reference
 * and the current place in the end result array, acting recursively
 */
function recursive($parent_key, $current_set, $parent_ids, $end_array) {
    foreach ($current_set as $parent_value) {
        if (!isSet($parent_ids[$parent_value])) {
            $end_array[$parent_key][] = $parent_value;
        } else {
            // if the parent_value is found in parent_ids, pass those values to the same function and the current end_array position
            $end_array[$parent_key] = recursive($parent_value, $parent_ids[$parent_value], $parent_ids, $end_array[$parent_key]);
        }
    }
    return $end_array;
}
// start with the top most element
$end_array = recursive(key($parent_ids), current($parent_ids), $parent_ids, $end_array);

print '<pre>'.
    print_r($parent_ids, true).
    print_r($end_array,true).
    '</pre>'
;

Outputs:

// resituated array
Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 4
            [2] => 6
        )

    [1] => Array
        (
            [0] => 2
        )

    [2] => Array
        (
            [0] => 3
        )

    [4] => Array
        (
            [0] => 5
        )

)

// the end result
Array
(
    [0] => Array
        (
            [1] => Array
                (
                    [2] => Array
                        (
                            [0] => 3
                        )

                )

            [4] => Array
                (
                    [0] => 5
                )

            [5] => 6
        )

)

You can make an array like this

$initialArray = array();
$initialArray[parent_id][cat_id]='your_value';   //$initialArray[0][0] ... and then increasing

If you want to form a recursive array you can refer on this page

Recursive function to generate multidimensional array from database result

this may help you a lot. Thanks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM