1

I have problem formatting array to become fancytree format, below array output from db:

array(44) {
  [0]=>
  array(3) {
    ["id"]=>
    string(2) "35"
    ["title"]=>
    string(28) "ROOT1"
    ["path"]=>
    string(28) "ROOT1"
  }
  [1]=>
  array(3) {
    ["id"]=>
    string(2) "36"
    ["title"]=>
    string(27) "SUB_A"
    ["path"]=>
    string(56) "ROOT1>SUB_A"
  }
  [2]=>
  array(3) {
    ["id"]=>
    string(2) "39"
    ["title"]=>
    string(12) "SUB_A_1"
    ["path"]=>
    string(69) "ROOT1>SUB_A>SUB_A_1"
  }
  [3]=>
  array(3) {
    ["id"]=>
    string(2) "37"
    ["title"]=>
    string(28) "SUB_A_2"
    ["path"]=>
    string(85) "ROOT1>SUB_A>SUB_A_2"
  }
  [4]=>
  array(3) {
    ["id"]=>
    string(2) "40"
    ["title"]=>
    string(30) "SUB_A_3"
    ["path"]=>
    string(87) "ROOT1>SUB_A>SUB_A_3"
  }
  [5]=>
  array(3) {
    ["id"]=>
    string(2) "43"
    ["title"]=>
    string(19) "SUB_A_4"
    ["path"]=>
    string(76) "ROOT1>SUB_A>SUB_A_4"
  }
  [6]=>
  array(3) {
    ["id"]=>
    string(2) "41"
    ["title"]=>
    string(12) "SUB_A_5"
    ["path"]=>
    string(69) "ROOT1>SUB_A>SUB_A_5"
  }
  [7]=>
  array(3) {
    ["id"]=>
    string(1) "1"
    ["title"]=>
    string(20) "ROOT2"
    ["path"]=>
    string(20) "ROOT2"
  }
  [8]=>
  array(3) {
    ["id"]=>
    string(2) "30"
    ["title"]=>
    string(37) "ROOT3"
    ["path"]=>
    string(37) "ROOT3"
  }
  [9]=>
  array(3) {
    ["id"]=>
    string(2) "34"
    ["title"]=>
    string(21) "SUB_B"
    ["path"]=>
    string(59) "ROOT3>SUB_B"
  }
  [10]=>
  array(3) {
    ["id"]=>
    string(2) "31"
    ["title"]=>
    string(15) "SUB_C"
    ["path"]=>
    string(53) "ROOT3>SUB_C"
  }

I want to format values of path become structured array, here is what I try:

function format(array $data) : array
{
    $single = [];
    // format to single data
    foreach ($data as $value) {
        $single[] = $value['path'];
    }

    $result = [];
    foreach ($single as $path) {
        $parts       = explode('>', $path);
        $section     = &$result;
        $sectionName = '';
        
        
        foreach ($parts as $part) {
            
            $sectionName = $part;
            
            if (array_key_exists($sectionName, $section) === FALSE) {
                $section[$sectionName] = [];
            }
                $section = &$section[$sectionName];
        }
    }

    return $result;
}

result:

array(3) {
  ["ROOT1"]=>
  array(1) {
    ["SUB_A"]=>
    array(5) {
      ["SUB_A_1"]=>
      array(0) {
      }
      ["SUB_A_2"]=>
      array(0) {
      }
      ["SUB_A_3"]=>
      array(0) {
      }
      ["SUB_A_4"]=>
      array(0) {
      }
      ["SUB_A_5"]=>
      array(0) {
      }
    }
  }
  ["ROOT2"]=>
  array(0) {
  }
  ["ROOT3"]=>
  array(2) {
    ["SUB_B"]=>
    array(0) {
    }
    ["SUB_C"]=>
    array(0) {
    }
  }

i need to include values of id and title to each path as expected result:

array(3) {
  [0]=>
  array(3) {
    ["title"]=>
    string(5) "ROOT1"
    ["id"]=>
    int(35)
    ["children"]=>
    array(1) {
      [0]=>
      array(3) {
        ["title"]=>
        string(5) "SUB_A"
        ["id"]=>
        int(36)
        ["children"]=>
        array(5) {
          [0]=>
          array(2) {
            ["title"]=>
            string(7) "SUB_A_1"
            ["id"]=>
            int(39)
          }
          [1]=>
          array(2) {
            ["title"]=>
            string(7) "SUB_A_2"
            ["id"]=>
            int(37)
          }
          [2]=>
          array(2) {
            ["title"]=>
            string(7) "SUB_A_3"
            ["id"]=>
            int(40)
          }
          [3]=>
          array(2) {
            ["title"]=>
            string(7) "SUB_A_4"
            ["id"]=>
            int(43)
          }
          [4]=>
          array(2) {
            ["title"]=>
            string(7) "SUB_A_5"
            ["id"]=>
            int(41)
          }
        }
      }
    }
  }
  [1]=>
  array(2) {
    ["title"]=>
    string(5) "ROOT2"
    ["id"]=>
    int(1)
  }
  [2]=>
  array(3) {
    ["title"]=>
    string(5) "ROOT3"
    ["id"]=>
    int(30)
    ["children"]=>
    array(2) {
      [0]=>
      array(2) {
        ["title"]=>
        string(5) "SUB_B"
        ["id"]=>
        int(34)
      }
      [1]=>
      array(2) {
        ["title"]=>
        string(5) "SUB_C"
        ["id"]=>
        int(31)
      }
    }
  }
}

I would be grateful for any help you are able to provide.

1 Answer 1

1

Here's how I would do it. Test

I used arrow functions to reduce the amount of lines and there are inline comments to explain what I did.

<?php

$data = [["id"=> "35", "title"=> "ROOT1", "path"=> "ROOT1"], ["id"=> "36", "title"=> "SUB_A", "path"=> "ROOT1>SUB_A"], ["id"=> "37", "title"=> "SUB_A_1", "path"=> "ROOT1>SUB_A>SUB_A_1"], ["id"=> "38", "title"=> "SUB_A_2", "path"=> "ROOT1>SUB_A>SUB_A_2"], ["id"=> "39", "title"=> "SUB_A_3", "path"=> "ROOT1>SUB_A>SUB_A_3"], ["id"=> "40", "title"=> "SUB_A_4", "path"=> "ROOT1>SUB_A>SUB_A_4"], ["id"=> "41", "title"=> "SUB_A_5", "path"=> "ROOT1>SUB_A>SUB_A_5"], ["id"=> "1", "title"=> "ROOT2", "path"=> "ROOT2"], ["id"=> "30", "title"=> "ROOT3", "path"=> "ROOT3"], ["id"=> "34", "title"=> "SUB_B", "path"=> "ROOT3>SUB_B"], ["id"=> "31", "title"=> "SUB_C", "path"=> "ROOT3>SUB_C"]];

// The path is defaulted to an empty string, so the regex would return the highest level elements only because they don't have any `>`
function get_children($data, $path = '') 
{
    $results = array_values(array_filter($data, fn($item) => preg_match("/^{$path}[^>]+$/", $item['path'])));
   // This regex, uses the passed path (e.g ROOT1) and searches for elements with anything but another `>`, because `>` signifies an inner directory.
   // So this would return elements with a path like `ROOT1>SUB_A, ROOT1>SUB_B` but not `ROOT1>SUB_A>SUB_A_1` 
    

    foreach($results as $i => $result) {
       // then I iterate through the result to get the element's children
        $children = array_values(get_children($data, "{$result['path']}>"));
        
        if (count($children)) $results[$i]['children'] = $children;
        unset($results[$i]['path']); // Removing the path key from the array
    }
    
    return $results;
}

$format = fn($data) => get_children($data);
print_r($format($data));
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for help, you make it so simple I can understand from your code comment.

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.