1

I'd like to generate a multi-dimensional array in PHP from the following -

- Item 1
-- Item 1.1
-- Item 1.2
--- Item 1.2.1
--- Item 1.2.2
---- Item 1.2.2.1
- Item 2
- Item 3
- Item 4
-- Item 4.1
-- Item 4.2

My eventual aim is to convert this string into an unordered list.

I'd imagine the best way to do this would be to create a recursive function. On a good day I'm sure I could work this out but I'm having a bit of a mind blank!

The array structure should be something like the below dump -

array(6) {
  [0]=>
  string(6) "Item 1"
  [1]=>
  array(3) {
    [0]=>
    string(8) "Item 1.1"
    [1]=>
    string(8) "Item 1.2"
    [2]=>
    array(3) {
      [0]=>
      string(10) "Item 1.2.1"
      [1]=>
      string(10) "Item 1.2.2"
      [2]=>
      array(1) {
        [0]=>
        string(12) "Item 1.2.2.1"
      }
    }
  }
  [2]=>
  string(6) "Item 2"
  [3]=>
  string(6) "Item 3"
  [4]=>
  string(6) "Item 4"
  [5]=>
  array(2) {
    [0]=>
    string(8) "Item 4.1"
    [1]=>
    string(8) "Item 4.2"
  }
}

Hope you can help.

7
  • google.com/… // most of the results seem to be for PHP, but since you did not even mention or tag a specific language, that’ll have to do then I guess. Commented Feb 14, 2017 at 12:28
  • @CBroe thanks for the Google link. It's PHP - sorry I forgot to include that so have added it now. This is a little more complex than your standard string recursion Commented Feb 14, 2017 at 12:30
  • From above comments, can you give a hint how the array should look like? Commented Feb 14, 2017 at 12:37
  • You don't need recursion if the text is always well formed. Just count the dashes on each line, and use that to determine when to output <ul> and </ul> tags. I'll write an answer when a get a moment. Commented Feb 14, 2017 at 12:40
  • @NirjharLo I've updated my question to include intended output. Commented Feb 14, 2017 at 13:01

1 Answer 1

2

The following will convert straight into HTML without recursion:

$text = array();
$text[] = '- Item 1';
$text[] = '-- Item 1.1';
$text[] = '-- Item 1.2';
$text[] = '--- Item 1.2.1';
$text[] = '--- Item 1.2.2';
$text[] = '---- Item 1.2.2.1';
$text[] = '- Item 2';
$text[] = '- Item 3';
$text[] = '- Item 4';
$text[] = '-- Item 4.1';
$text[] = '-- Item 4.2';

$previous_dash_count = 0; // topmost parent
foreach ($text as $line) {
    if (preg_match('/(^\-+)(.*)/', $line, $matches, PREG_OFFSET_CAPTURE)===1) {
        $dash_count = strlen($matches[1][0]);
        $title = $matches[2][0];

        if ($dash_count == $previous_dash_count) {
            echo "<li>$title</li>\n";
        } elseif ($dash_count > $previous_dash_count) {
            echo str_repeat("<ul>\n", $dash_count - $previous_dash_count);
            echo "<li>$title</li>\n";
        } else {
            echo str_repeat("</ul>\n",$previous_dash_count-$dash_count+1);
            echo "<ul>\n";
            echo "<li>$title</li>\n";
        }

        $previous_dash_count = $dash_count;
    }
}
echo str_repeat("</ul>\n",$previous_dash_count);

I make several assumptions. That the input text always behaves well and doesn't contain randomness. Also I don't assume UTF-8 text, but you're safe with dashes.

Here is the array version in all its gory glory:

$stack = array();
$previous_dash_count = 0;
$parent_node = array();
foreach ($text as $line) {
    if (preg_match('/(^\-+)(.*)/', $line, $matches, PREG_OFFSET_CAPTURE)===1) {
        $dash_count = strlen($matches[1][0]);
        $title = $matches[2][0];

        if ($dash_count == $previous_dash_count) {
            $parent_node[] = $title;
        } elseif ($dash_count > $previous_dash_count) {
            for ($push_count = $previous_dash_count; $push_count<$dash_count; $push_count++) {
                array_push($stack, $parent_node); // remember node
                $new_child = array();
                $new_child[] = $title;
                $parent_node[] = $new_child;
                $parent_node = $new_child;
            }
        } else {
            for ($pop_count = $previous_dash_count; $pop_count >$dash_count; $pop_count--) {
                $old_child = $parent_node;
                $parent_node = array_pop($stack);
                $parent_node[] = $old_child;
            }
            $parent_node[] = $title;
        }

        $previous_dash_count = $dash_count;
    }
}
for ($pop_count = $previous_dash_count; $pop_count > 0; $pop_count--) {
    $old_child = $parent_node;
    $parent_node = array_pop($stack);
    $parent_node[] = $old_child;
}

print_r($parent_node);

We keep a stack of array nodes, so we have a link between a child and its parent. Note the structure of this code is identical to that for the straight HTML version.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks - I had to go for this kind of method in the end as it was the easiest. Slightly different technique but your answer is right nonetheless. Cheers!
Awesome on the array version too, thank you so much! Cudos :)
That's it - I had already given it a crack but you know those days when you're just really struggling to see the solution?!...

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.