Home  »  Solutions  »  One Pass Parent-Child Array Structure
Preview of One Pass Parent-Child Array Structure

One Pass Parent-Child Array Structure

Nate Weiner - Posted in Solutions, , , Comments (18)

During the time that I’ve been programming I’ve continually run into times when I needed to be able to store a multi-level list into a database and retrieve it. I’ve used this in a CMS system to allow uses to create and nest their own menu options or to allow users to create and nest categories of items. Saving it was never a problem, but retrieving it and building a PHP array with nested structure was a messy task.

We want to turn this:

Database Table: Items
Id Parent Id Name
1 0 North America
2 1 Canada
3 7 California
4 0 Europe
5 0 Antartica
6 3 Los Angeles
7 1 United States
8 7 Florida
9 3 Hollywood
10 2 Quebec
Into this:

  • Antartica
  • North America
    • Canada
      • Quebec
    • United States
      • California
        • Hollywood
        • Los Angeles
    • Florida
  • South America

The problem was that you couldn’t easily enter a variable four levels deep into an array without knowing all of the parents of that item. This was okay if you retrieved the data in order of parents, but that meant if you wanted to sort the data by another column (such as name), you would have to do multiple passes through your data to continully reshuffle it. And this is a huge drain of cpu cycles on even medium sized data-sets.

The Solution

Let me show you what it looks like:

$refs = array();
$list = array();

$sql = "SELECT item_id, parent_id, name FROM items ORDER BY name";
$result = mysql_query($sql);
while($data = @mysql_fetch_assoc($result)) {
    $thisref = &$refs[ $data['item_id'] ];

    $thisref['parent_id'] = $data['parent_id'];
    $thisref['name'] = $data['name'];

    if ($data['parent_id'] == 0) {
        $list[ $data['item_id'] ] = &$thisref;
    } else {
        $refs[ $data['parent_id'] ]['children'][ $data['item_id'] ] = &$thisref;
    }
}

How it Works

PHP References

This solution’s core is based on using PHP References. A reference allows you to access the same variable but with different names. In other words, it allows you to assign aliases to a variable. This way, any changes made to the reference will also affect the original variable.

Here’s an example:

$var = ‘Hello World’;
$ref = &$var; //you define a reference by using ‘&’
$ref = ‘Goodbye World’;
print “var = ” . $var;
print “ref = ” . $ref;Outputs:
var = Goodbye World
ref = Goodbye World

Two Lists

To solve this, we are going to use not one, but two arrays. We will have our main array which is the one we are building with multilple levels, and secondly, we will have a single-level array that is all references to parts of our main list. This will allow us to target deep inside of the array without knowing the depth, or all of the parents above the level.

Let me explain how this will look in a more visual way.

On the left, we have our references array and on the right we have the list we are building with multiple levels. Now you have two ways to make an adjustment to a variable deep inside the main array, such as adding ‘Miami’ to Floria.

$main_list['North America']['children']['United States']['children']['Florida]['children'][] = ‘Miami’;
or
$references['Florida']['children'][] = ‘Miami’;

You can see how it is much easier to target deep inside of the array using the reference, rather having to know the entire structure above where Florida is.

All Together Now

So when you extract the data from the database you will do the following process, repeating until all the data has been processed.

  1. Add item to reference list
  2. If item has no parent (meaning it’s a top level item), add item reference to top level of build list.
  3. If item has a parent, add the item reference to it’s parents list of children.

Watch It Step by Step

If I’ve completely lost you, I suggest that you take a look how this works step by step. I’ve provided an example of building the location list above, where it prints out what the reference and main list look like after each item as been added. You can view that here.

Comments (18)


  1. Thanks for the tip! Will keep that in mind.

    November 8th, 2007 Alex K
  2. This is great. However I haven’t used arrays for stuff like this much in the past so I am trying to get my head around what you do with the two arrays afterwards to sort and display the data. Do you have an example of how you did the example list above? Thx!

    November 19th, 2007 Jim Starkweather
  3. So, how do I get the values out of the arrays?

    December 11th, 2007 Shawn
  4. I did my own version of this using recursive functions. It’s a tiny bit longer… five to ten lines more.

    December 14th, 2007 fcb
  5. Thank! I worked on this problem last week.
    It’s a great solution!

    December 20th, 2007 Snowcore
  6. This is great… thank you

    February 14th, 2008 Timmy
  7. Works just fine. Kind of weird formatting at the end (having the children ABOVE the name/id), but usable none the less. Cheers!

    May 7th, 2008 Noah
  8. same as for Shawn : how do you guys manage to have a nice indented display ?

    June 18th, 2008 Tom
  9. Oh, dude - it is a brilliant idea !
    Thanks a lot for sharing this.
    Best wishes.

    June 19th, 2008 IVO GELOV
  10. Many thanks, I would be very Thankful if you could show us
    what is the script u used to echo
    this:

    * Antartica
    * North America
    o Canada
    + Quebec
    o United States
    + California
    # Hollywood
    # Los Angeles
    o Florida
    * South America

    regardless the depth childs may reach?

    August 28th, 2008 Alaa Abdelhaq
  11. i’ve spent the time to find the best function to display the tree so you dont have to:
    http://kevin.vanzonneveld.net/techblog/article/convert_anything_to_tree_structures_in_php/

    September 13th, 2008 display tree
  12. you are a PHP god. Thanks!!

    December 2nd, 2008 star_faeiz
  13. Thanks, very useful. Here’s a snippet to create a set of nested lists out of the $list array:

    <?php
    function toUL($arr) {
    $html = ” . PHP_EOL;
    foreach ($arr as $v) {
    $html .= ” . $v['name'] . ” . PHP_EOL;
    if (array_key_exists(’children’, $v)) {
    $html .= toUL($v['children']);
    }
    }
    $html .= ” . PHP_EOL;
    return $html;
    }
    echo toUL($list);
    ?>

    February 2nd, 2009 Ronald
  14. Thanks, very useful. Here’s a snippet to create a set of nested lists out of the $list array:

    February 2nd, 2009 Ronald
  15. May I ask a question?
    Where does ['children'] come from?
    Is it php or mysql? Any help will be appreciated it.
    Thanks for a useful writing.

    October 5th, 2009 shin
  16. Very intelligent work!. SO much better than recursion.

    October 30th, 2009 Eric
  17. Nice, pass by reference is something I often forget to think about, but use often in class extensions. Thanks for the tutorial. Jeff

    January 18th, 2010 Jeff Walters
  18. Well Done! This is always what I really like to archive. Simply but powerful! Thx for sharing~ God bless you.

    January 29th, 2010 Shiro

Leave a Reply