3

I've an array of folders/paths:

$arr = Array
(
    0 => Array
         (
              'name' => 'aaa'
         ),

    1 => Array
         (
              'name' => 'aaa\bbb'
        ),

    2 => Array
         (
              'name' => 'aaa\bbb\ccc'
         ),
    3 => Array
         (
              'name' => 'ddd'
         )
);

I'd like to transform it to multidimensional (tree-like) array (keeping the structure: index/key & value/name):

 aaa 
    bbb
       ccc
 ddd

Any suggests?

Ken
  • 1,605
  • 3
  • 13
  • 20
  • If the first array is `0=>'aaa' 1=>'aaa\bbb' 2=>'aaa\ddd\ccc'` how do you want the output to look? Or is that never an issue because they're always going to be the same? – NorthGuard Jun 16 '11 at 14:24
  • Sorry. I've corrected my example. – Ken Jun 16 '11 at 14:30
  • I find your decision to put the deepest level element as value and all parents as key somewhat weird. How are you going to know what to take from the array - key or value? – mkilmanas Jun 16 '11 at 14:32
  • Your original array doesn't even have `ddd` element but the transformed has one. – anubhava Jun 16 '11 at 14:32
  • @mkilmanas: any recommendations? – Ken Jun 16 '11 at 14:39

4 Answers4

5

Try:

$arr = array(
   array('name' => 'aaa'),
   array('name' => 'aaa\bbb'),
   array('name' => 'aaa\bbb\ccc'),
   array('name' => 'ddd'),
   array('name' => 'ddd\zzz'),
   array('name' => 'zzz'),
   array('name' => 'ddd\zzz\fff'),
);

$new = array();
$helper = array();
foreach ($arr as $i => $entry) {
    $parent =& $new;
    /**
     * One could use:
     *   explode(DIRECTORY_SEPARATOR, $entry['name'])
     * 
     * instead of '\\' if you're dealing with file-structures
     */
    foreach ($path = explode('\\', $entry['name']) as $ii => $element) {
        $subPath = implode('.', array_slice($path, 0, $ii + 1));

        if (isset($helper[$subPath])) {
            $parent =& $helper[$subPath];
            continue;
        }

        $parent[$i] = array('name' => $element);
        $helper[$subPath] =& $parent[$i];
    }
}

print_r($new);

Output:

Array
(
    [0] => Array
        (
            [name] => aaa
            [1] => Array
                (
                    [name] => bbb
                    [2] => Array
                        (
                            [name] => ccc
                        )

                )

        )

    [3] => Array
        (
            [name] => ddd
            [4] => Array
                (
                    [name] => zzz
                    [6] => Array
                        (
                            [name] => fff
                        )

                )

        )

    [5] => Array
        (
            [name] => zzz
        )

)
Yoshi
  • 54,081
  • 14
  • 89
  • 103
  • I love this solution, very nicely done. My only recommendation is possibly to use `DIRECTORY_SEPARATOR` instead of a constant `'\\'` – Brad Christie Jun 16 '11 at 15:01
  • @Brad Christie Thanks :) About the `DIRECTORY_SEPARATOR`; I didn't really know how to interpret (the meaning) of the string, so I just took them as they were. But I guess you're right, so I'll update the code. :) – Yoshi Jun 16 '11 at 15:04
  • Cool, but how to keep the structure? – Ken Jun 16 '11 at 15:07
  • @Yoshi: I'm going to _assume_ (and you know what happens when we do that) the paths are being generated by a PHP method, so they will remain in the native format. But you may be absolutely right, they make have no relevance on the OS itself, in which case the PHP constant may break it. – Brad Christie Jun 16 '11 at 15:08
  • @Ken What do you mean? (The `name` index ??) – Yoshi Jun 16 '11 at 15:09
  • @Yoshi: yes, name & index and rename *aaa\bbb* to *bbb*, *aaa\bbb\ccc* to *ccc*. – Ken Jun 16 '11 at 15:12
  • @Ken See the updated code. Hope I understood your intent correctly. – Yoshi Jun 16 '11 at 15:29
0
$newarr=array();

foreach ($arr as $element) {
    $currentroot=$newarr;
    $pieces=explode('\\', $element);
    for ($x=0; $x<=count($pieces); x++) {
        $currentroot[$pieces[$x]]=array();
        $currentroot=$currentroot[$pieces[$x]];
    }
}

Untested but should get you started. You need to add a condition to check if it is the last piece, and make it a string value instead of an array.

Salman A
  • 262,204
  • 82
  • 430
  • 521
Brad
  • 159,648
  • 54
  • 349
  • 530
0

For the new requirements:

$arr = array(
   array('name' => 'aaa'),
   array('name' => 'aaa\bbb'),
   array('name' => 'aaa\bbb\ccc'),
   array('name' => 'ddd'),
);

function traverse(array $array) {
    $mark=array_shift($array);
    return array($mark => $array ? traverse($array) : array() );
}

$out = array();
foreach($arr as $path)
{
    ($add=traverse(explode('\\',$path['name'])))
        && $out[key($add)]=current($add)
        ;
}

Output:

array(2) {
  ["aaa"]=>
  array(1) {
    ["bbb"]=>
    array(1) {
      ["ccc"]=>
      array(0) {
      }
    }
  }
  ["ddd"]=>
  array(0) {
  }
}

Old Question:

The old question had these reqs:

$arr = array(
   0 => 'aaa',
   1 => 'aaa\bbb',
   2 => 'aaa\bbb\ccc',
);

function traverse(array $array) {
    $mark=array_shift($array);
    return $array ? array($mark => traverse($array)) : $mark;
}

$out = array();
foreach($arr as $path)
{
    is_array($add=traverse(explode('\\',$path)))
        && $out[key($add)]=current($add)
        ;
}

Tested, giving this output:

array(1) {
  ["aaa"]=>
  array(1) {
    ["bbb"]=>
    string(3) "ccc"
  }
}
hakre
  • 193,403
  • 52
  • 435
  • 836
  • is it possible to rename *aaa\bbb* to *bbb*, *aaa\bbb\ccc* to *ccc*? – Ken Jun 16 '11 at 15:27
  • @Ken Would be possible in the input array, yes. But for the output array, the keys are already in that form. – hakre Jun 16 '11 at 15:33
0

Well, had to do it in two functions, but here goes:

// Directory Array to Hierarchy
function _DAtoH($path, $result = null)
{    
  if (empty($path))      return array();
  if (is_null($result))  $result = array();

  $path = explode(DIRECTORY_SEPARATOR, $path);
  $curr = array_shift($path);
  if (!isset($result[$curr]))
    $result[$curr] = array();
  $result[$curr] = _DAtoH(implode(DIRECTORY_SEPARATOR, $path), $result[$curr]);
  return $result;
}
function DAtoH($arr)
{
  $result = array();
  foreach ($arr as $a)
    $result = _DAtoH($a,$result);
  return $result;
}

Passing the bottom function (the _DAtoH is just a recursive helper) the array you specified in your original question (var_dump(DAtoH($arr));), you should receive:

array(2) {
  ["aaa"]=>
  array(2) {
    ["bbb"]=>
    array(1) {
      ["ccc"]=>
      array(0) {
      }
    }
    ["fff"]=>
    array(0) {
    }
  }
  ["ddd"]=>
  array(1) {
    ["eee"]=>
    array(0) {
    }
  }

}

(Note: I added some folder paths just to test it out, thus the fff, eee, etc.)

Brad Christie
  • 100,477
  • 16
  • 156
  • 200