Help structuring an array

I’m not sure I’ve run into a situation such as this one before. I went to sleep on it and still don’t have anything for it. Here are the requirements:

I have a function which will return an array (an exploded URI slug actually):


array('blog', 'category', 'title');

I need a loop to move this in to a new array of this format:


$new = array(
    'blog' => array(
        'children' => array(
            'category' => array(
                 'children' => array(
                      'title' => array(
                           'children' => array()
                      )
                  )
             )
        )
    )
)

The loop cannot be invasive upon existing content, because I will be looping through multiple slugs to add them to this same structure (through another loop I’ll be adding title2 under category).

Are you sure you need that children key? Keys need to be unique.

Try this:


  
<?php 
  $uri = array('blog', 'category', 'title');  


  $new = array
  (
    'blog' => array('children' => array
    (
        'category' => array('children' => array
        (
          'title' => array( 'children' => array() )
          )
        )
      )
    )
  );

echo '<pre>';
  $jb = array(); 


  $cnt = count($uri)-1; 
  for($i2=0;  $i2<=$cnt; $i2++):
    if($i2===0): 
      // echo '<br /><b>$jb = '.$cnt, '-', $i2 ,' ==> </b>'; 
      $jb[] = array($uri[$cnt-$i2] => array('children' => array() ) );


    else: 
      // echo '<br />$jb = '.$cnt, '-', $i2 ,' ==> '; 
      $jb[] = array($uri[$cnt-$i2] => array('children' => $jb[$i2-1] ) ); 
    endif;
    // print_r($jb);
  endfor;  


  $jb = $jb[$cnt];
  echo '$uri = '; print_r($uri);
  echo '===================================================================';
  echo '<br />$new = '; print_r($new); 
  echo '===================================================================';
  echo '<br />$jb = '; print_r($jb); 
echo '<pre>';

Output


$uri = Array
(
  [0] => blog
  [1] => category
  [2] => title
)
===================================================================


$new = Array
(
  [blog] => Array
  (
    [children] => Array
    (
      [category] => Array
      (
        [children] => Array
        (
          [title] => Array
          (
            [children] => Array()
          )
        )
      )
    )
  )
)
===================================================================
$jb = Array
(
  [blog] => Array
  (
    [children] => Array
    (
      [category] => Array
      (
        [children] => Array
        (
          [title] => Array
          (
            [children] => Array()
          )
        )
      )
    )
  )
)



The idea is that the children array will contain anything below it in the uri.

/foo/bar/baz
/foo/bar/boo

baz and boo are both children of bar, which is a child of foo.

The key ‘children’ is unique

Dah

So the keys would be literal child names and not actually children.

<?php 
$input = array('programming', 'php', 'Dealing with arrays');
$format = array('blog', 'category', 'title');


function add_children2(array $format, array $parts){
	$new = array();
	$sub = array();
	
	foreach($format as $k => $f):
			$sub[] = $f;
			$sub[] = $parts[$k];
	endforeach;
	
	$c = &$new;
	foreach ($sub as $v) {
	    $c[$v] = array();
	    $c = &$c[$v];
	}
	$c = null;
	
	return $new;
}
$result2 = add_children2($format,$input);  


echo "<pre>";
print_r($result2);  
echo "</pre>"; 
?>

Outputs

Array
(
    [blog] => Array
        (
            [programming] => Array
                (
                    [category] => Array
                        (
                            
```php
 =&gt; Array
                                (
                                    [title] =&gt; Array
                                        (
                                            [Dealing with arrays] =&gt; 
                                        )

                                )

                        )

                )

        )

)

And for dealing with extra titles, e.g. subtitle you can add extra if condition.

<?php
$input = array('programming', 'php', 'Dealing with arrays');
$format = array('blog', 'category', 'title', 'subtitle');


function add_children2(array $format, array $parts){
	$new = array();
	$sub = array();
	
	foreach($format as $k => $f):
			$sub[] = $f;
			if(array_key_exists($k,$parts)):
				$sub[] = $parts[$k];
			endif;
	endforeach;
	
	$c = &$new;
	foreach ($sub as $v) {
	    $c[$v] = array();
	    $c = &$c[$v];
	}
	$c = null;
	
	return $new;
}
$result2 = add_children2($format,$input);


echo "<pre>";
print_r($result2);
echo "</pre>";
?>

Result

Array
(
    [blog] => Array
        (
            [programming] => Array
                (
                    [category] => Array
                        (
                            
```php
 =&gt; Array
                                (
                                    [title] =&gt; Array
                                        (
                                            [Dealing with arrays] =&gt; Array
                                                (
                                                    [subtitle] =&gt;
                                                )

                                        )

                                )

                        )

                )

        )

)

If I follow the question correctly, I’d use something like this:


$a = array('foo', 'baz', 'bar');
$b = array('foo', 'baz', 'ban');

function adder($current, $input)
{
    $level = &$current;
    foreach ($input as $v)
    {
        if (!isset($level[$v]))
        {
            $level[$v] = array('children' => array());
        }
        $level = &$level[$v]['children'];
    }
    return $current;
}

$c = adder(array(), $a);
$c = adder($c, $b);

echo '<pre>';
print_r($c);
echo '</pre>';

The main trick here is $level = &$level[$v]['children'];, so that when you start with a element in the array the pointer is set to the ‘children’ sub-array of which ever key came before.

Output of this example is


Array
(
    [foo] => Array
        (
            [children] => Array
                (
                    [baz] => Array
                        (
                            [children] => Array
                                (
                                    [bar] => Array
                                        (
                                            [children] => Array
                                                (
                                                )

                                        )

                                    [ban] => Array
                                        (
                                            [children] => Array
                                                (
                                                )

                                        )

                                )

                        )

                )

        )

)

Hey Kyle,

It had me scratching my head for a while, but I think I’ve come up with a good solution. I’ve written a recursive function to convert an array of slugs into the nested array structure you wanted. Then you can use PHP’s [fphp]array_merge_recursive[/fphp] function to merge with an existing array without losing data.

The function:


function build_nested_array(array $input)
{
	if (count($input) == 1) {
		return array($input[0] => array('children' => array()));
	}

	return array($input[0] => array('children' => build_nested_array(array_slice($input, 1))));
}

Example:


$slugs1 = build_nested_array(array('blog', 'category', 'title'));
$slugs2 = build_nested_array(array('blog', 'category', 'frogs'));
$slugs3 = build_nested_array(array('blog', 'tags', 'cheese'));

$result = array_merge_recursive($slugs1, $slugs2, $slugs3);

echo print_r($result);

Output:


Array
(
    [blog] => Array
        (
            [children] => Array
                (
                    [category] => Array
                        (
                            [children] => Array
                                (
                                    [title] => Array
                                        (
                                            [children] => Array
                                                (
                                                )
                                        )
                                    [frogs] => Array
                                        (
                                            [children] => Array
                                                (
                                                )
                                        )
                                )
                        )
                    [tags] => Array
                        (
                            [children] => Array
                                (
                                    [cheese] => Array
                                        (
                                            [children] => Array
                                                (
                                                )
                                        )
                                )
                        )
                )
        )
)

Those are some nice examples gentlemen.

Thanks for the replies guys, you all got my noggin rolling again with some very nice examples. I’m not done yet as I’m still wokring on some design decisions, but I can tell you that this is for the next release of https://github.com/KyleWolfe/PHPRouter. I ran into a few design flaws when working on a REST API a few weeks ago, so I’m making some updates and plan on using this in a few more upcoming projects. I’ll have a new branch up with the changes I’ve made here in the next few hours and will link to it.

Here’s where its at right now: https://github.com/KyleWolfe/PHPRouter/tree/1.0

I went with the array_merge option just to get it done. There’s some issues however, such as if one were to redefine a route. I keep bouncing back and forth between this setup vs moving the children into the Route object. I’m still a little torn, but I will rewrite another branch with that method to see how I like it, either way, much to clean up on it.