skip to Main Content

Convert indented text to multi dimensional array in php. I have tried some examples for indenting
text to PHP multi dimensional array but to no avail. Here is what I did so far. The first text in string has no indent and after that 3 space indents and so on.

<?php

function convertIndentedTextToArray($text) {
    $lines = explode("n", $text);
    $result = [];
    $stack = [];
    
    foreach ($lines as $line) {
        $indent = 0;
        while (substr($line, 0, 1) === " ") {
            $indent++;
            $line = substr($line, 1);
        }
        
        $node = ['value' => trim($line), 'children' => []];
        
        while ($indent < count($stack)) {
            array_pop($stack);
        }
        
        if (empty($stack)) {
            $result[] = &$node;
        } else {
            $parent = &$stack[count($stack) - 1];
            $parent['children'][] = &$node;
        }        
        $stack[] = &$node;
    }
    
    return $result;
}

I also tried this example

$string = <<<EOT
Hey again I am on root
   Hey it's wonderful
  This is good time
  I love the way you are
Hey again I am on root  
EOT;

$lines = explode("n", $string);
$output = [];
$currentNode = &$output;

$indent1 = 1;
$elementIndex = 0; // Initialize index for ManagedElements

$hold = [];
$cnt=0;$cnt2=0;
$incre=1;

$first=0;
foreach ($lines as $line) {
    $trimmedLine = trim($line);
    $indentationLevel = strlen($line) - strlen($trimmedLine);

    if ($indentationLevel == 0) {
        $elementIndex++; // Increment index for each new ManagedElement
        $hold[$elementIndex]['parent'][] = trim($line);

    } else if ($indentationLevel == 3) {
        $hold[$elementIndex]['parent']['child'][] = trim($line);
        $cnt=count($hold[$elementIndex]['parent']['child'])-$incre;
    } else if ($indentationLevel == 6) {
        $hold[$elementIndex]['parent']['child']['child'][$cnt][] = trim($line);
        $cnt2=count($hold[$elementIndex]['parent']['child']['child'])-$incre;
    }
    else if ($indentationLevel == 9) {
        $hold[$elementIndex]['parent']['child']['child']['child'][$cnt2][] = trim($line);
    }
}

echo "<pre>";
print_r($hold);
echo "</pre>";

Input:

$input = "Hello world I am good
 Hey it's wonderful
  This is good time
   I love the way you are
Hey again I am on root";

But I am getting output with children array blank:

array (
  0 => 
  array (
    'value' => 'Hey again I am on root',
    'children' => 
    array (
    ),
  ),
  1 => 
  array (
    'value' => 'Hey again I am on root',
    'children' => 
    array (
    ),
  ),
)

This is the expected output:

array (
  0 => 
  array (
    'value' => 'Hey again I am on root',
    'children' => 
    array (
      'value'=>'Hey it's wonderful',
      'children'=>array()
    ),
    array (
      'value'=>'This is good time',
      'children'=>array('value'=>'I love the way you are','children'=>array())
    ),
  ),
  1 => 
  array (
    'value' => 'Hey again I am on root',
    'children' => 
    array (
    ),
  ),
)

3

Answers


  1. Your code was over-cluttered, but basically your string wasn’t presented in the same format that the code requires, i.e. a multiple of 3 leading spaces and no trailing spaces.

        <?php
        
        $string = <<<EOT
        Hey again I am on root
           Hey it's wonderful
           This is good time
              I love the way you are
        Hey again I am on root
        EOT;
        
        $lines = explode("n", $string);
        $output = [];
        $elementIndex = 0; // Initialize index for ManagedElements
               
        foreach ($lines as $line) {
            $trimmedLine = trim($line);
            $indentationLevel = strlen($line) - strlen($trimmedLine);
        
            if ($indentationLevel == 0) {
                $elementIndex++; // Increment index for each new ManagedElement
                $output[$elementIndex]['parent'][] = trim($line);
            } else if ($indentationLevel == 3) {
                $output[$elementIndex]['parent']['child'][] = trim($line);
            } else if ($indentationLevel == 6) {
                $output[$elementIndex]['parent']['child']['child'][] = trim($line);
            }
            else if ($indentationLevel == 9) {
                $output[$elementIndex]['parent']['child']['child']['child'][] = trim($line);
            }
        }
        
        print_r($output);
    

    (https://www.tehplayground.com/zmiKIdTfNqXapTaP)

    produces

    Array
    (
        [1] => Array
            (
                [parent] => Array
                    (
                        [0] => Hey again I am on root
                        [child] => Array
                            (
                                [0] => Hey it's wonderful
                                [1] => This is good time
                                [child] => Array
                                    (
                                        [0] => I love the way you are
                                    )
    
                            )
    
                    )
    
            )
    
        [2] => Array
            (
                [parent] => Array
                    (
                        [0] => Hey again I am on root
                    )
    
            )
    
    )
    
    Login or Signup to reply.
  2. Ok, I have made this dynamic with respect to spaces.

    The way I have done it is to

    • Not use stack like structure since the copy by value and copy by reference can create clumsy code and would be quite unreadable with respect to what sits in the stack and what goes into the final result.

    • calculate the no. of leading spaces in the string.

    • Go to the deeply nested kid or child array till leading spaces’ times and find the correct child to add the current text or line to.

    • While traversing, always find the last kid of the current array since that is where the current one is going to be added to.

    Snippet:

    <?php
    
    $input = <<<EOT
    Hello world I am good
     Hey it's wonderful
      This is good time
       I love the way you are
    Hey again I am on root
    EOT;
    
    $d = array_filter(explode("n", $input));
    
    $result = [];
    
    foreach($d as $line){
        $leadingSpaces = strlen($line) - strlen(ltrim($line));
        
        $currKid = &$result;
        
        while($leadingSpaces-- > 0){
            if(empty($currKid['children'])){
                $currKid = &$currKid['children'];
            }else{
                $currKid = &$currKid['children'][count($currKid['children']) - 1];
            }
        }
        
        $currKid['children'][] = [
            'value' => $line,
            'children' => []
        ];
    }
    
    $result = $result['children'] ?? []; // remove "children" key from first level if desired.
    
    print_r($result);
    

    Live Demo

    Login or Signup to reply.
  3. Make sure to trim ending spaces before comparing strlen($line) - strlen($trimmedLine), as strlen('Hey I am on root ') != strlen(trim('Hey I am on root '))

    I have also added a new key in your structure to simplify the logic.

    <?php
    $string = <<<EOT
    Hey I am on root
     Hey it's wonderful
      This is good time
      I love the way you are
    Hey again I am on root  
    EOT;
    
    $lines = explode("n", $string);
    $lines = array_map('rtrim', $lines);
    $out = [];
    $iter = 0;
    
    foreach($lines as $row => $line) {
        $trimmedLine = trim($line);
    
        if (strlen($line) - strlen($trimmedLine) === 0) {
            $iter = $row;
            $out[$row] = ['value' => $trimmedLine, 'identation' => 0, 'children' => []];
            continue;
        }
        
        $out[$iter]['children'] = findChild($out[$iter], $trimmedLine, strlen($line) - strlen($trimmedLine));
    }
    
    echo "<pre>";
    print_r($out);
    echo "</pre>";
    
    function findChild(array $data, $value, int $identation) {
        if (empty($data['children'])) {
            $data['children'][] = ['value' => $value, 'identation' => $identation, 'children' => []];
            return $data['children'];
        }
        
        foreach($data['children'] as $index => $child) {
            
            if ($child['identation'] < $identation) {
                $data['children'][$index]['children'] = findChild($child, $value, $identation);
                
                return $data['children'];
            } else {
                $data['children'][] = ['value' => $value, 'identation' => $identation, 'children' => []];
                
                return $data['children'];
            }
        }
    }
    

    Live Demo

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search