1

We have an array with the following values:

$a = array("a", "a.b", "a.b.c", "X", "X.Y", "X.Y.Z");

And the goal is, to make modify the first array into the following structure:

$a = array(
     "a" => array(
         "b" => array(
             "c" => array(),
         ),
     ),
     "X" => array(
         "Y" => array(
             "Z" => array(),
         ),
     ),
);

Why i am asking? On of my customer has a table for shop categories. And these categories are in one column (simplified!):

 +-----------------------+
 |id |       name        |
 +---|-------------------+
 | 4 | A                 |
 | 5 | A.B               |
 | 6 | A.B.C             |
 | 7 | X                 |
 | 8 | X.Y               |
 | 9 | X.Y.Z             |
 +-----------------------+

How can i do this with PHP?

EDIT:

My current "solution / trys"

<?php

$arr = array(
    "a",
    "a.b",
    "a.b.c",
    "x",
    "x.y",
    "x.y.z",
);

$container = array();
$updateMe = array();

foreach($arr as $key => $value) {
    $cleanName = explode(".", $value);

    foreach($cleanName as $keyArray => $valueArray) {
        for($c = 0;$c<$keyArray+1;$c++) {
            $updateMe[$cleanName[$c]] = array();
        }


    } 

    $container[$cleanName[0]] = $updateMe;
    unset($updateMe);
}

echo "<pre>";
var_dump($container);
echo "===\r\n";

My output:

array(2) {
  ["a"]=>
  array(3) {
    ["a"]=>
    array(0) {
    }
    ["b"]=>
    array(0) {
    }
    ["c"]=>
    array(0) {
    }
  }
  ["x"]=>
  array(3) {
    ["x"]=>
    array(0) {
    }
    ["y"]=>
    array(0) {
    }
    ["z"]=>
    array(0) {
    }
  }
}
===

SOLUTION

<?php

$arr = array(
    "a",
    "a.b",
    "a.b.c",
    "x",
    "x.y",
    "x.y.z",
);

$array = array();
$test = array();

foreach($arr as $key => $text) {
    $array = array();
    foreach(array_reverse(explode('.', $text)) as $key) $array = array($key => $array);

    $test[] = $array;
}

echo "<pre>";
var_dump($test);
echo "===\r\n";
Tyralcori
  • 1,079
  • 13
  • 33
  • And what is your question? – Felix Kling Jan 08 '15 at 20:52
  • Your arrray doesn't makes sense! – Rizier123 Jan 08 '15 at 20:53
  • It is not clear why you original array has 6 elements but your result array has only 2. – Lorenz Meyer Jan 08 '15 at 20:57
  • @FelixKling to create associative array layers into each other (see second array) Rizier123 i know :/ LorenzMeyer Sry it's just an example. I'm gonna modify this. – Tyralcori Jan 08 '15 at 21:03
  • Multiple values in a single database column? Bleh. Is there anyway you can modify this database? A linking structure of `shops`, `categories` and the join `shops_categories` would be great here. – Tim Lewis Jan 08 '15 at 21:07
  • You could probably do something with array_reduce, but it wouldn't be the most efficient code. – T0xicCode Jan 08 '15 at 21:12
  • Of course, it is! But at least we're talking about 1kk records, and i don't wanna modify them without any script.. The first step was, to get all category records only. And the first array is my result (simplified).. – Tyralcori Jan 08 '15 at 21:13
  • *"How can i do this with PHP?"* Have you tried *anything* at all? – Felix Kling Jan 08 '15 at 21:17
  • Yep. Let me edit my questing, sry. – Tyralcori Jan 08 '15 at 21:19
  • 1
    In your try, you are only ever setting one level deep in your array. You need to go deeper. – gen_Eric Jan 08 '15 at 21:31
  • 1
    possible duplicate of [PHP - Make multi-dimensional associative array from a delimited string](http://stackoverflow.com/questions/9627252/php-make-multi-dimensional-associative-array-from-a-delimited-string) – scrowler Jan 08 '15 at 21:32
  • 2
    You can get a pretty good result from using [the answer from this question](http://stackoverflow.com/questions/9627252/php-make-multi-dimensional-associative-array-from-a-delimited-string), example: https://eval.in/240648, or even shorter is the [second answer in that question](http://stackoverflow.com/a/9627318/2812842), example: https://eval.in/240649 – scrowler Jan 08 '15 at 21:34

4 Answers4

2

I like using references in PHP. This might not be the best solution, but it seems to work.

<?php
$arr = array(
    "a",
    "a.b",
    "a.b.c",
    "x",
    "x.y",
    "x.y.z",
);

$output = array();

foreach($arr as $path){
    // Create a reference to the array
    // As we go deeper into the path we will move this reference down
    $setArray =& $output;

    foreach(explode('.', $path) as $key){
        // If this key does not exist, create it
        if(!isset($setArray[$key])){
            $setArray[$key] = array();
        }

        // Move the reference down one level,
        // so that the next iteration will create
        // the key at the right level
        $setArray =& $setArray[$key];
    }
}

// Destroy the reference, so that we don't accidently write to it later
unset($setArray);

var_dump($output);
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • 2
    Another great answer! The advantage for this solution is, that there is no duplicated content / duplicated layers / elements inside the array! – Tyralcori Jan 08 '15 at 21:43
1

You can use the accepted answer from this question, or this answer from the same question to get a good starting point (I'll use the second answer because it's shorter in this example).

$out = array();
foreach ($a as $string) {
    $array = array();
    foreach(array_reverse(explode('.', $string)) as $key) {
        $array = array($key => $array);
    }
    $out[] = $array;
}

This will give you a numeric key based array, so then you can shift off the first level of the array using an answer from this question:

$out = call_user_func_array('array_merge', $out);

And your result:

Array
(
    [a] => Array
        (
            [b] => Array
                (
                    [c] => Array
                        (
                        )

                )

        )

    [X] => Array
        (
            [Y] => Array
                (
                    [Z] => Array
                        (
                        )

                )

        )

)
Community
  • 1
  • 1
scrowler
  • 24,273
  • 9
  • 60
  • 92
0

This should work for you:

<?php

    $a = array("a", "a.b", "a.b.c", "1", "1.2", "1.2.3");
    $results = array();

    function stringToArray($path) {

        $pos = strpos($path, ".");

        if ($pos === false)
            return array($path => "");

        $key = substr($path, 0, $pos);
        $path = substr($path, $pos + 1);

        return array(
            $key => stringToArray($path)
        );

    }

    foreach($a as $k => $v) {
        if(substr_count(implode(", ", $a), $v) == 1)
            $results[] = stringToArray($v);
    }

    $results = call_user_func_array('array_merge', $results);
    print_R($results);

?>

Output:

Array ( [a] => Array ( [b] => Array ( [c] => ) ) [0] => Array ( [2] => Array ( [3] => ) ) )
Rizier123
  • 58,877
  • 16
  • 101
  • 156
0

just tricky one

  $a = array("a", "a.b", "a.b.c", "X", "X.Y", "X.Y.Z");

    $res = array();
    foreach ($a as $str) {
        $str = str_replace('.','"]["',$str);
        $tricky = '$res["'.$str.'"]';
        eval ($tricky.' = array();');
};
Alex
  • 16,739
  • 1
  • 28
  • 51