sybercoda
sybercoda

Reputation: 565

How to build a multidimensional array tree from an associative array in PHP?

I'm trying to build a multidimensional array tree from an associative array by replacing the 's' and 'd' key string values with their respective key 'bandnumber' arrays but can't seem to crack it. I've only been able to make it work for the first node of the array.

For example, I have the following array:

$coiArray = array (
    array('bandnumber' => '02-BELG-2129929', 's'  =>'94-BELG-3237180', 'd' => '96-BELG-3156295' ),
    array('bandnumber' => '94-BELG-3237180', 's'  =>'88-BELG-3206112', 'd' => '88-BELG-3206173' ),
    array('bandnumber' => '88-BELG-3206112', 's'  =>'81-BELG-3238253', 'd' => '87-BELG-3008002' ),
    array('bandnumber' => '88-BELG-3206173', 's'  =>'', 'd' => '' ),
    array('bandnumber' => '96-BELG-3156295', 's'  =>'88-BELG-3206112', 'd' => '85-BELG-3049648' ),
    array('bandnumber' => '85-BELG-3049648', 's'  =>'', 'd' => '' ),
    array('bandnumber' => '81-BELG-3238253', 's'  =>'', 'd' => '' ),
    array('bandnumber' => '87-BELG-3008002', 's'  =>'', 'd' => '' ),
);

And I'm trying to programmatically convert the above array into the following multidimensional array tree:

$coiNestedArray = array('bandnumber' => '02-BELG-2129929',
               's' => array('bandnumber' => '94-BELG-3237180',
                     's' => array('bandnumber' => '88-BELG-3206112',
                           's' => array('bandnumber' => '81-BELG-3238253',
                                                 's' =>'',
                                                 'd' => ''
                           ),
                           'd' => array('bandnumber' => '87-BELG-3008002',
                                                 's' =>'',
                                                 'd' => ''
                           )
                     ),
                     'd' => array('bandnumber' => '88-BELG-3206173',
                           's' =>'',
                           'd' => ''
                     )
               ),
               'd' => array('bandnumber' => '96-BELG-3156295',
                     's' => array('bandnumber' => '88-BELG-3206112',
                           's' => array('bandnumber' => '81-BELG-3238253',
                                                 's' =>'',
                                                 'd' => ''
                           ),
                           'd' => array('bandnumber' => '87-BELG-3008002',
                                                 's' =>'',
                                                 'd' => ''
                           )
                     ),
                     'd' => array('bandnumber' => '85-BELG-3049648',
                           's' =>'',
                           'd' => ''
                     )
               )
        );

This is the closest I've come thus far, but it only updates the first node of the array:

function findKey($coiarray, $bandnumber){
    $thisCol = array_column($coiarray, 'bandnumber');
    $found_key = array_search($bandnumber, $thisCol);
    return $found_key;
}


foreach ($coiArray as $key => $value) {

    $s = '';

    $found_key = findKey($coiArray,$coiArray[$key]['s']);
    if(isset($coiArray[$found_key])){
        $s = $coiArray[$found_key];
    }

    $d = '';

    $found_key = findKey($coiArray,$coiArray[$key]['d']);
    if(isset($coiArray[$found_key])) {
        $d = $coiArray[$found_key];
    }

    $coiArray[$key] = array('bandnumber' => $coiArray[$key]['bandnumber'], 's'  => $s, 'd' => $d );

}

I will re-frame from posting the entire dump of the array here, but this is the first node of the $coiArray, from var_dump($coiArray), and you will notice all of the innermost nested ["s"] and ["d"] keys are strings instead of their respective arrays.

[0]=>
  array(3) {
    ["bandnumber"]=>
    string(15) "02-BELG-2129929"
    ["s"]=>
    array(3) {
      ["bandnumber"]=>
      string(15) "94-BELG-3237180"
      ["s"]=>
      string(15) "88-BELG-3206112"
      ["d"]=>
      string(15) "88-BELG-3206173"
    }
    ["d"]=>
    array(3) {
      ["bandnumber"]=>
      string(15) "96-BELG-3156295"
      ["s"]=>
      string(15) "88-BELG-3206112"
      ["d"]=>
      string(15) "85-BELG-3049648"
    }
  }

The example below is the first node from the $coiNestedArray, which I created manually, to illustrate what I'm trying to achieve. Notice that every ["s"] and ["d"] is an array, derived from $coiArray.

array(3) {
  ["bandnumber"]=>
  string(15) "02-BELG-2129929"
  ["s"]=>
  array(3) {
    ["bandnumber"]=>
    string(15) "94-BELG-3237180"
    ["s"]=>
    array(3) {
      ["bandnumber"]=>
      string(15) "88-BELG-3206112"
      ["s"]=>
      array(3) {
        ["bandnumber"]=>
        string(15) "81-BELG-3238253"
        ["s"]=>
        string(0) ""
        ["d"]=>
        string(0) ""
      }
      ["d"]=>
      array(3) {
        ["bandnumber"]=>
        string(15) "87-BELG-3008002"
        ["s"]=>
        string(0) ""
        ["d"]=>
        string(0) ""
      }
    }
    ["d"]=>
    array(3) {
      ["bandnumber"]=>
      string(15) "88-BELG-3206173"
      ["s"]=>
      string(0) ""
      ["d"]=>
      string(0) ""
    }
  }

How do I solve this problem?

Upvotes: 1

Views: 1368

Answers (1)

trincot
trincot

Reputation: 350262

You would need to create an associative array keyed by bandnumbers, so you can directly find the row by bandnumbers. Then visit the children and replace each child with the corresponding value in that associative array, by reference.

Optionally detect which bandnumber was not referenced ever as a child: it is the root. But if you know the root bandnumber, or you know it is always the one in the first input row, then you can skip that last step. Finally extract the value of that root (assuming there is exactly one):

// Key the rows by their bandnumber:
foreach($coiArray as $row) {
    $hash[$row["bandnumber"]] = $row;
}
foreach($hash as &$row) {
    // Replace children with the corresponding row in the hash
    foreach(["s","d"] as $prop) {
        $child = $row[$prop];
        if (!isset($hash[$child])) continue;
        $row[$prop] =& $hash[$child];
        $children[] = $child; // Keep track of non-root bandnumbers
    }
}
// Only needed when you don't know which bandnumber is the root:
$root = current(array_diff(array_keys($hash), $children, ["s","d"]));

$result = $hash[$root];

Upvotes: 1

Related Questions