Home > Net >  Array to tree structure with no Id just string as keys (is this possible?)
Array to tree structure with no Id just string as keys (is this possible?)

Time:03-08

I've been failing (my whole day) in getting this structure formatted as a tree to present in this format, see picture attached.

as this has not parent and child the names of the key values strings like pattern_type_name act like parents and pattern_name are the child and under pattern_name are the marker_description to present the structure.

structure to display

any help would be appreciated

below is the code I'm using:

$data='[
{
"pattern_type_name":"Blood Sugar",
"pattern_name":"TEST",
"marker_description":"A\/G ratio"
},
{
"pattern_type_name":"Blood Sugar",
"pattern_name":"TEST",
"marker_description":"Albumin"
},
{
"pattern_type_name":"Blood Sugar",
"pattern_name":"TEST",
"marker_description":"Alk Phos"
},
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST3",
"marker_description":"A\/G ratio"
},
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST3",
"marker_description":"Albumin"
},
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST3",
"marker_description":"Alk Phos"
},
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST3",
"marker_description":"BUN"
},
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST3",
"marker_description":"BUN\/Creat ratio"
},
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST3",
"marker_description":"Calcium"
},
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST3",
"marker_description":"Chloride"
},
{
    "pattern_type_name":"Cardivascular",
    "pattern_name":"TEST1",
    "marker_description":"EX3DWSQ"
    },
{
"pattern_type_name":"Red Blood Cell",
"pattern_name":"TEST4",
"marker_description":"FEXTAFIX"
}
]';


 $related_patterns=json_decode($data,true);
 $arrlengng= count($related_patterns);
 $patternCount=0;
 $filteredItems=array();
 $current_pattern_type_name=$related_patterns[0] 
 ['pattern_type_name'];
 $current_pattern_name=$related_patterns[0]['pattern_name'];

 function filterArrayByKeyValue($array, $key, $keyValue){
     return array_filter($array, function($value) use ($key, 
     $keyValue) {
   return $value[$key] == $keyValue; 
  });
 }

 for($contloop=0;$contloop < $arrlengng;$contloop  ){

   if ($related_patterns[$contloop] 
       ['pattern_type_name']==$current_pattern_type_name){
    echo '==Pattern Type: '.$related_patterns[$contloop]['pattern_type_name'].' valor='.$contloop.'<br>';
    echo '===Name: '.$related_patterns[$contloop]['pattern_name'].'<br>';
    $current_pattern_name=$related_patterns[$contloop]['pattern_name'];
    $current_pattern_type_name=$related_patterns[$contloop]['pattern_type_name'];
    $filteredItems = filterArrayByKeyValue($related_patterns, 'pattern_name', $current_pattern_name);
    while($patternCount < count($filteredItems)){
        echo '====marker_description: '.$filteredItems[$patternCount]['marker_description'].'<br>';
        $patternCount  ;
    }       
}
$patternCount=0;
echo $contloop.'------->'.$current_pattern_type_name.'------>'.$related_patterns[$contloop]['pattern_type_name'].'<br>';

}

CodePudding user response:

When you need to translate data from one form to another, break it into steps:

  1. Discover the data's structure
  2. How can you translate-- how do the elements shift from one to the other
  3. Traverse the data
  4. Build the new structure

So the first thing I notice about the data is that it is already packaged in JSON, which is very convenient. How is it structured?

$data = '[
{
"pattern_type_name":"Blood Sugar",
"pattern_name":"TEST",
"marker_description":"A\/G ratio"
},
// ... snip
]';

$table = json_decode($data); // illustrating with objects, it's easier to understand
print_r($table);

yields:

Array
(
    [0] => stdClass Object
        (
            [pattern_type_name] => Blood Sugar
            [pattern_name] => TEST
            [marker_description] => A/G ratio
        )

    [1] => stdClass Object
        (
            [pattern_type_name] => Blood Sugar
            [pattern_name] => TEST
            [marker_description] => Albumin
        )

// etc

So immediately I see that each row (the indexed array) is an object, with each row having the same properties. You could also picture it this way. with each property as a column:

     type            name   description
0    Blood Sugar     TEST   A\G ratio 
1    Blood Sugar     TEST   Albumin
2    Blood Sugar     TEST   Alk Phos
3    Red Blood Cell  TEST3  A/G ratio
4    Red Blood Cell  TEST3  Albumin
5    Red Blood Cell  TEST3  Alk Phos
6    Red Blood Cell  TEST3  BUN
7    Red Blood Cell  TEST3  BUN/Creat ratio
// etc

If you think of each column's value as a key, you could think of it this way (not using the row index):

$table['Blood Sugar']['TEST']['A\G ratio '];
$table['Blood Sugar']['TEST']['Albumin'];
$table['Blood Sugar']['TEST']['Alk Phos'];
$table['Red Blood Cell']['TEST3']['A/G ratio'];
$table['Red Blood Cell']['TEST3']['Albumin'];
$table['Red Blood Cell']['TEST3']['Alk Phos'];
$table['Red Blood Cell']['TEST3']['BUN'];
$table['Red Blood Cell']['TEST3']['BUN/Creat ratio'];

This isn't valid code, of course; it's not assigning values. What's important is the structure. (in fact the value doesn't matter at all; it could just be a boolean true.)

Hopefully now the translation has become obvious; as you traverse $table, the desired order is going to just fall in place:

$out = [];
foreach($tempTable as $type=>$names) {
    $out[] = "-$type";
    foreach($names as $name=>$descriptions) {
        $out[] = "--$name";
        foreach($descriptions as $description=>$bool) {
            $out[] = "----$description";
        }
    }
}

print join("\n",$out);

Now that we have our plan, we need to prepare the data. Fortunately, the data is already in rows; all we need to do is make a new data structure with the values.

$table = json_decode($data, true);  // using associative arrays this time

$tempTable = [];
foreach($tempTable as $row) {
    $type = $row['pattern_type_name'];
    $name = $row['pattern_name'];
    $desc = $row['marker_description'];
    $tempTable[$type][$name][$desc] = true;  // value doesn't really matter
}

Note, this is where Barmar's suggestion comes into play; instead of building an intermediate structure, he suggests printing it as you go by comparing each iteration's value against the previous. I'm accomplishing the same thing by making the new array dedupe the keys.

Putting it all together, we get this

$tempTable = [];
$table = json_decode($data, true);  // using associative arrays this time
foreach($table as $row) {
    $type = $row['pattern_type_name'];
    $name = $row['pattern_name'];
    $desc = $row['marker_description'];
    $tempTable[$type][$name][$desc] = true;  // value doesn't really matter
    // print "tempTable[][$type][$name][$desc] = true;\n";
}

// print_r($tempTable);  // this is how I discovered that the index shouldn't be there!
// die;

$out = [];
foreach($tempTable as $type=>$names) {
    $out[] = "-$type";
    foreach($names as $name=>$descriptions) {
        $out[] = "--$name";
        foreach($descriptions as $description=>$bool) {
            $out[] = "----$description";
        }
    }
}

print join("\n",$out);

Last note: I personally would not be satisfied with the nested foreach loops... the picket fence is a signal that it could be done better; but for this exercise I think it's sufficient.

  • Related