2

From these four arrays:

$root = (FORD, FIAT, GM, KIA, FERRARI);  
$parentIsFiat = (Panda, Punto, Tipo);  
$parentIsPanda = (1, 13, 16, 20);  
$parentIs13 = (Red, Blue, White);  

How can I create a multidimensional array to give me this:

FORD  
FIAT  
--Panda  
----1  
----13  
------Red  
------Blue  
------White  
----16  
----20  
--Punto  
--Tipo       
GM  
KIA  
FERRARI

Background: The current menu on my site has every link on the site in the HTML. I want to only have the HTML for menu items that are actually visible. At the moment I get each item in the path (FIAT > Panda > 13) and it's siblings with code similar to this:

$categoryPath = \XLite\Core\Database::getRepo('\XLite\Model\Category')->getCategoryPath($this->getCategoryId());  
foreach ($categoryPath as $category) {
    $currentCatID = $category->getID();
    $currentCatSiblings = $category->getSiblings(true);
    foreach ($currentCatSiblings as $sibling) {
        $menuItems[$currentCatID][] = $sibling->getName(); // the four arrays above
    }
}

Each of the arrays has the parent ID as the key so I can 'attach' it in the correct place but I don't know how to build the array from my four existing arrays.

2
  • So is red, blue or white the last item in the $categoryPath? Or how else will you get those entries? Commented Jun 12, 2017 at 20:34
  • Yep. \XLite\Core\Database::getRepo('\XLite\Model\Category')->getCategoryPath($this->getCategoryId()); returns the category path from root to the active category as category objects. Commented Jun 13, 2017 at 9:47

2 Answers 2

2

You could introduce a temporary $parent variable that will reference the place in the tree where the siblings must be inserted, and move that $parent reference further down the tree for the next "generation" of siblings:

$tree = [];
$parent = &$tree; // Use `= &` to reference the same location
foreach ($categoryPath as $category) {
    $currentCatID = $category->getID();
    $currentCatSiblings = $category->getSiblings(true);
    foreach ($currentCatSiblings as $sibling) {
        $parent[] = [ "name" => $sibling->getName() ];
        if ($sibling->getID() === $currentCatID) $index = count($parent) - 1;
    }
    $parent[$index]["children"] = [];
    $parent = &$parent[$index]["children"]; // Use `= &` to reference the deeper location
}

At the end $tree will contain the nested array. It will have "children" keys to store the nested structure, like this:

[
  [ "name" => "Ford" ],
  [
    "name" => "Fiat",
    "children" => [
      [
        "name" => "Panda",
        "children" => [
          [ "name" => "1" ],
          [
            "name" => "13",
            "children" => [
              [ "name" => "Red" ],
              [
                "name" => "Blue",
                "children" => [],
              ],
              [ "name" => "White" ],
            ],
          ],
          [ "name" => "16" ],
          [ "name" => "20" ],
        ],
      ],
      [ "name" => "Punto" ],
      [ "name" => "Tipo"  ],
    ],
  ],
  [ "name" => "GM" ],
  [ "name" => "Kia" ],
  [ "name" => "Ferrari" ],
]

Or, if you prefer to have the names used as keys in an associative array, then:

$tree = [];
$parent = &$tree; // Use `= &` to reference the same location
foreach ($categoryPath as $category) {
    $currentCatID = $category->getID();
    $currentCatSiblings = $category->getSiblings(true);
    foreach ($currentCatSiblings as $sibling) {
        $parent[$sibling->getName()] = [];
    }
    $parent = &$parent[$category->getName()]; // Use `= &` to reference the deeper location
}

Result:

[
  "Ford" => [],
  "Fiat" => [
    "Panda" => [
      "1" => [],
      "13" => [
        "Red" => [],
        "Blue" => [],
        "White" => []
      ],
      "16" => [],
      "20" => []
    ],
    "Punto" => [],
    "Tipo" => []
  ],
  "GM" => [],
  "Kia" => [],
  "Ferrari" => []
]
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for that. The second example appears to be working in my real application. I need to study the code a bit to work out how it works - I always struggle with arrays. Just to note.. in your results the Panda is appearing under Ford instead of Fiat.
0
$root = ["FORD", "FIAT", "GM", "KIA", "FERRARI"];
$parentIsFiat = ["Panda", "Punto", "Tipo"];
$parentIsPanda = [1, 13, 16, 20];
$parentIs13 = ["Red", "Blue", "White"];

$desiredOutput = [];

foreach($root as $make){
    $desiredOutput[$make] = [];
}

foreach($parentIsFiat as $model){
    $desiredOutput["FIAT"][$model] = [];
}

foreach($parentIsPanda as $year){
    $desiredOutput["FIAT"]["Panda"][$year] = [];
}

foreach($parentIs13 as $color){
    $desiredOutput["FIAT"]["Panda"][13][$color] = [];
}


var_dump($desiredOutput);

Yields:

array(5) {
  ["FORD"]=>
  array(0) {
  }
  ["FIAT"]=>
  array(3) {
    ["Panda"]=>
    array(4) {
      [1]=>
      array(0) {
      }
      [13]=>
      array(3) {
        ["Red"]=>
        array(0) {
        }
        ["Blue"]=>
        array(0) {
        }
        ["White"]=>
        array(0) {
        }
      }
      [16]=>
      array(0) {
      }
      [20]=>
      array(0) {
      }
    }
    ["Punto"]=>
    array(0) {
    }
    ["Tipo"]=>
    array(0) {
    }
  }
  ["GM"]=>
  array(0) {
  }
  ["KIA"]=>
  array(0) {
  }
  ["FERRARI"]=>
  array(0) {
  }
}

The key to remember is that all arrays in PHP are more like a "dictionary" or "hash set" type in other languages. PHP does not have an "array" as other languages (Java, C, Javascript, ...) have them. If you want an "array" so you can use syntax like echo $myStuff[3]; then you simply create a PHP array (a/k/a "dictionary") where every key is a successive integer.

Example to look under the hood in any array:

$anyArray = getAnyArray();
foreach($anyArray as $key=>$value){
    echo($key . ":" . $value . "<br>");
}

3 Comments

To be clear, this answer is not robust/recursive; it uses a static number of loops. This will break when there are not four levels to the output array.
Yep. I assumed OP's difficulty was with understanding how keys and values work.
I appreciate your answer but yep.. I was hoping to see something recursive or with a PHP array manipulation function like merge() or similar.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.