Link to home
Start Free TrialLog in
Avatar of acicovic
acicovicFlag for Greece

asked on

Convert nested array to HTML list (ul)

Hi,

I am searching for a recursive function which will convert a nested array to a valid HTML UL list like this:


<ul>
    <li>Category 1
        <ul>
            <li>Category 1 - Subcategory 1</li>
            <li>Category 1 - Subcategory 2</li>
        </ul>
    </li>
    <li>Category 2
        <ul>
            <li>Category 2 - Subcategory 1</li>
            <li>Category 2 - Subcategory 2
                <ul>
                    <li>test</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

The array from which to create this structure would be: http://stackoverflow.com/questions/1591338/recursively-creating-a-multi-dimensional-array-in-php#tab-top (post by John Kugelman) - the array declaration/processing code is there for you to test.  If you have any other better alternatives, let me know.
Avatar of themrrobert
themrrobert
Flag of United States of America image


Damn this code was a pain in the ass to write

<?php

$html = '';

function makeChild($array) {
  $r = "\n\t<li><a href=\"${array[ItemLink]}\">${array[ItemText]}</a></li>";
  if(count($array['Children']) > 0)
   foreach($array['Children'] as $child)
    $r .= '<li><ul>' . makeChild($child) . '</ul></li>';
 
}

$html = "<ul>" . makeChild($masterArray) . "</ul>";


?>

Open in new window

Deceptively simple, it required a lot of thinking.
Avatar of acicovic

ASKER

Hi themrrobert,

It doesn't work for me. (I've tested with my own array and the one I have provided from StackOverflow).  

From what I gather, only the first line is executed ( $r = "\n\t<li><a href=\"${array[ItemLink]}\">${array[ItemText]}</a></li>"; )  After that, the function doesn't find any ['Children'] subarray into the array, making it stop.
Avatar of svenarild
svenarild

The code below should be able to go through an unlimited depth. Recursive functionality is pretty simple, so I am certain you will get the hang of it pretty soon.

The code is based on the array from the website you linked to, but it can easily be changed to fit any similar array.

Let me know if you have any questions.
$menuItems = array
(
    1 => array
    (
        'ItemText' => 'Home',
        'ItemLink' => 'index.php',
        'ParentID' => null,
    ),

    2 => array
    (
        'ItemText' => 'Home Sub 1',
        'ItemLink' => 'somepage.php',
        'ParentID' => 1,
    ),

    3 => array
    (
        'ItemText' => 'Home Sub 2',
        'ItemLink' => 'somepage2.php',
        'ParentID' => 1,
    ),

    4 => array
    (
        'ItemText' => 'Contact',
        'ItemLink' => 'contact.php',
        'ParentID' => null,
    ),
);


function processMenu($data) {
	$menu = array();

	foreach($data as $key => $value) {
		if (!is_null($value['ParentID'])) {
			$menu[$value['ParentID']][$key] = array();
			}
		else {
			$menu[$key] = array();
			}
		}

	createUL($menu, $data);
	}

function createUL($menu, $data) {
	echo '<ul>';

	foreach ($menu as $key => $value) {
		echo '<li>';
		
		echo $data[$key]['ItemText'];
					
		if (!empty($value)) {
			createUL($value, $data);
			}

		echo '</li>';
		}

	echo '</ul>';
	}

processMenu($menuItems);

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of svenarild
svenarild

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi svenarild,

Excellent code, I cannot thank you enough!  I would like to make a slight modification to the output from this:

    <ul>
        <li>Category 1
            <ul>
                <li>Category 1 - Subcategory 1</li>
                <li>Category 1 - Subcategory 2</li>
            </ul>
        </li>
        <li>Category 2
            <ul>
                <li>Category 2 - Subcategory 2</li>
                <li>Category 2 - Subcategory 1
                    <ul>
                        <li>test</li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>

To this:

        <ul>
            <li>Category 1</li>     <!-- added a li closing tag -->
            <li>                             <!-- added a li opening tag -->
                <ul>
                    <li>Category 1 - Subcategory 1</li>
                    <li>Category 1 - Subcategory 2</li>
                </ul>
            </li>
            <li>Category 2</li>     <!-- added a li closing tag -->
            <li>                             <!-- added a li opening tag -->
                <ul>
                    <li>Category 2 - Subcategory 2</li>
                    <li>Category 2 - Subcategory 1</li>    <!-- added a li closing tag -->
                    <li>                                                       <!-- added a li opening tag -->
                        <ul>
                            <li>test</li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul>

Would that be easy?  The reason is to make it easier to style with CSS.

Thanks!
That is simple enough, you just need to move the bottom closing li up before the recursive function, and wrap the recursive function with a starting and ending li tag.


$menuItems = array
(
    1 => array
    (
        'ItemText' => 'Home',
        'ItemLink' => 'index.php',
        'ParentID' => null,
    ),

    2 => array
    (
        'ItemText' => 'Home Sub 1',
        'ItemLink' => 'somepage.php',
        'ParentID' => 1,
    ),

    3 => array
    (
        'ItemText' => 'Home Sub 2',
        'ItemLink' => 'somepage2.php',
        'ParentID' => 1,
    ),


    4 => array
    (
        'ItemText' => 'Contact',
        'ItemLink' => 'contact.php',
        'ParentID' => null,
    ),
	5 => array
    (
        'ItemText' => 'Home Sub 2 - 1',
        'ItemLink' => 'contact.php',
        'ParentID' => 3,
    ),
	 6 => array
    (
        'ItemText' => 'Home Sub 2 - 1 - 1',
        'ItemLink' => 'contact.php',
        'ParentID' => 5,
    ),
	7 => array
    (
        'ItemText' => 'Home Sub 2 - 2',
        'ItemLink' => 'contact.php',
        'ParentID' => 3,
    ),
);


function processMenu($data) {
	$menu = array();

	foreach($data as $key => $value) {
		if (!is_null($value['ParentID'])) {
			if (isset($menu[$value['ParentID']])) {
				$menu[$value['ParentID']][$key] = array();
				}
			else {
				//Locate the correct path to the menu in question
				$array = array($value['ParentID']);

				processMenuDeep($array, $value, $data);

				$num = count($array) - 1;

				$temp = &$menu[$array[$num--]];

				for (;$num>=0;--$num) {
					if ($num == 0) {
						$temp[$array[$num]][$key] = array();
						}
					else {
						$temp = &$temp[$array[$num]];
						}
					}
				}
			}
		else {
			$menu[$key] = array();
			}
		}

	createUL($menu, $data);
	}

function processMenuDeep(&$array, $value, $data) {
	if (!is_null($data[$value['ParentID']]['ParentID'])) {
		$array[] = $data[$value['ParentID']]['ParentID'];

		processMenuDeep($array, $data[$value['ParentID']], $data);
		}	
	}

function createUL($menu, $data) {
	echo '<ul>';

	foreach ($menu as $key => $value) {
		echo '<li>';
		
		echo $data[$key]['ItemText'];
			
		echo '</li>';			

		if (!empty($value)) {
			echo '<li>';

			createUL($value, $data);

			echo '</li>';
			}
		}

	echo '</ul>';
	}

processMenu($menuItems);

Open in new window

Thank you! :)