Page 1 of 1
Working with Multidimensional Arrays
Posted: Sat Jan 22, 2011 1:04 pm
by s992
I'm having trouble wrapping my head around this problem.
I'm trying to write some code to generate dynamic navigation which will have an unlimited number of subnavigation levels. The way I'm currently approaching this is with a multi-dimensional array of navigation items. This is a simplified example of the array:
Code: Select all
$navigation = array(
'link' => '#',
'title' => 'Top Level Navigation',
array(
'link' => '#',
'title' => 'SubNav1',
array(
'link' => '#',
'title' => 'SubSubNav1')
)
);
As you can see, child elements are arrays underneath parent elements. I'd like this to go on as deep as possible, but I have no clue how to process the final array. An endless string of foreachs? Any advice would be fantastic!
Re: Working with Multidimensional Arrays
Posted: Sat Jan 22, 2011 1:41 pm
by John Cartwright
I would probably use a key to identify child elements, and recursively iterate to build your html using the following structure.
Code: Select all
$navigation = array(
array(
'link' => '#',
'title' => 'Top Level Navigation',
'children' => array(
array(
'link' => '#',
'title' => 'SubNav1',
'children' => array(
array(
'link' => '#',
'title' => 'SubSubNav1'
)
)
)
)
)
);
function buildNav($nav)
{
foreach ($nav as $value) {
$html .= '<a href="'. $value['link'] .'">'. $value['title'] .'</a>';
if (isset($value['children']) && count($value['children'])) {
$html .= buildNav($value['children']);
}
}
return $html;
}
echo buildNav($navigation);
You will obviously need to modify the buildNav() to properly build the html, but you should get the idea.
Re: Working with Multidimensional Arrays
Posted: Sat Jan 22, 2011 6:51 pm
by s992
Hey John, thanks - worked like a charm!
Now, I do have another question. I thought I had my array creation set up properly but it looks like I do not - my nav is only going two levels deep. Here's the function I've got currently(this is in CodeIgniter, but it shouldn't make a difference):
Code: Select all
protected function _get_nav_array() {
// SELECT slug, id, parent_id, title FROM nav WHERE display = 1
$sql = $this->CI->db->select('slug, id, parent_id, title')->where('display',1)->get('nav');
// Turn the result into an array
$arr = $sql->result_array();
// Put a placeholder value in the [0] index of the array because
// everything was offset by 1
array_unshift($arr,'placeholder');
// Loop through the array
foreach($arr as $item) {
// parent_id = 0 indicates that it is a top-level nav element
// and nothing further is required
if($item['parent_id'] != 0) {
// Example: $array[1]['children'][0] = $item
$arr[$item['parent_id']]['children'][] = $item;
// Pop that off the end of the array so we don't have duplicate values
array_pop($arr);
}
}
// Shift the placeholder index back off of the top
array_shift($arr);
// Just a setter.
$this->_set_nav_array($arr);
}
So the problem, which I believe lies in my use of array_pop(), is that any element that has a parent_id of another subnav element gets destroyed at some point during the process. Here's an example of what my table looks like:
Code: Select all
id parent_id title slug display
1 0 About Us about 1
2 0 Demo Page demo-page 1
3 1 Subnav1 sub 1
4 1 Subnav2 subnav 1
5 2 Other Subnav subnavv 1
6 3 SubSubNav subsub 1
Everything is displaying as expected except for the final entry on the table which has parent_id set to 3. Any suggestions?
Re: Working with Multidimensional Arrays
Posted: Sun Jan 23, 2011 8:45 am
by John Cartwright
Like I showed you in my example, you need to recursively call the function and pass the child elements to the parent. You can do this without recursion, but it's the simplest way

Re: Working with Multidimensional Arrays
Posted: Sun Jan 23, 2011 11:02 am
by s992
Hey John, thanks again. I managed to track down
this question on StackOverflow that got me pointed in the right direction. The final function, in case anyone ever runs into this problem:
Code: Select all
protected function _get_nav_array() {
// SELECT slug, id, parent_id, title FROM nav WHERE display = 1
$sql = $this->CI->db->select('slug, id, parent_id, title')->where('display',1)->get('nav');
// Turn the result into an array
$arr = $sql->result_array();
// Put a placeholder value in the [0] index of the array because
// everything was offset by 1, thanks to MySQL starting IDs at
// 1 and PHP starting array indexes at 0
$placeholder = array('id' => NULL, 'parent_id' => NULL, 'title' => NULL, 'slug' => NULL);
array_unshift($arr,$placeholder);
// Loop through the array, finding parent_id and assigning sub-nav
// items as necessary
foreach($arr as $id => &$item) {
if($item['parent_id'] != NULL) {
$arr[$item['parent_id']]['children'][$id] = &$item;
}
}
// Get rid of all the left over items that were duplicated during the
// previous foreach loop
foreach(array_keys($arr) as $id) {
if($arr[$id]['parent_id'] != NULL) {
unset($arr[$id]);
}
}
// Shift the placeholder index back off of the top
array_shift($arr);
// Just a setter.
$this->_set_nav_array($arr);
}