Indented list to multidimensional array

I was surprised not to find an answer to this on SO (or elsewhere on the internet for that matter). It concerns a nested indented list which I want to convert into a multidimensional array according to the level of indentation.

By way of an example, here is some sample input:

Home
Products
    Product 1
        Product 1 Images
    Product 2
        Product 2 Images
    Where to Buy
About Us
    Meet the Team
    Careers
Contact Us

Ideally I’d like to feed this into some (recursive?) function and get the following output:

array(
    'Home' => array(),
    'Products' => array(
        'Product 1' => array(
            'Product 1 Images' => array(),
        ),
        'Product 2' => array(
            'Product 2 Images' => array(),
        ),
        'Where to Buy' => array(),
    ),
    'About Us' => array(
        'Meet the Team' => array(),
        'Careers' => array(),
    ),
    'Contact Us' => array(),
);

I’m confused by the logic required to perform such a task, so any help would be appreciated.

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

As it’s still unclear if you’re trying to read from some given structure (html-dom) or from the given string as plain text, I assumed it’s the string you’re trying to parse. If so, try:

<?php
$list =
'Home
Products
    Product 1
        Product 1 Images
    Product 2
        Product 2 Images
    Where to Buy
About Us
    Meet the Team
    Careers
Contact Us';

function helper($list, $indentation = '    ') {
  $result = array();
  $path = array();

  foreach (explode("\n", $list) as $line) {
    // get depth and label
    $depth = 0;
    while (substr($line, 0, strlen($indentation)) === $indentation) {
      $depth += 1;
      $line = substr($line, strlen($indentation));
    }

    // truncate path if needed
    while ($depth < sizeof($path)) {
      array_pop($path);
    }

    // keep label (at depth)
    $path[$depth] = $line;

    // traverse path and add label to result
    $parent =& $result;
    foreach ($path as $depth => $key) {
      if (!isset($parent[$key])) {
        $parent[$line] = array();
        break;
      }

      $parent =& $parent[$key];
    }
  }

  // return
  return $result;
}

print_r(helper($list));

Demo: http://codepad.org/zgfHvkBV

Solution 2

I’m not going to write the recursive function, but to point you in a helpful direction, take a look at PHP’s substr_count function. Based on this, you could count the number of tabs in front of each line and compare it with the number of tabs in the previous line to figure out if it’s a child, sibling, etc.

Solution 3

Am I the only one with a beautiful mind seeing the pattern?

The input array is almost the same thing!, with the lines ending in only 3 possible ways: opening, whole, or closing parentheses.

$L = 4;//estimate depth level

function tabbed_text_to_array ($raw, $L) {

        $raw = preg_replace("/^(\\t*)([^\\t\\n]*)\\n?/m" ,  "\t$1'$2' => array(\n" , $raw );

    for( ; $L > 0 ; $L-- ) {

        for( $i=0; $i<3 ;$i++ ) {
            $preL = $L-1;
            $s = array( "^(\\t{{$L}})([^\\t\\),]*)(?=\\n\\t{{$L}}')", "^(\\t{{$L}})([^\\t\\),]*)(?=\\n(\\t{0,{$preL}})')", "^(\\t{{$L}})(\\),)(?=\\n(\\t{{$preL}})[^\t])" );
            $r = array(    "$1$2),"   ,     "$1$2)\n" . str_repeat("\t",$preL) . "),"    ,    "$1),\n$3),"    );
            $raw = preg_replace( "/$s[$i]/m" , $r[$i], $raw );
        }
    }
    return "array(\n". $raw. ")\n);";   

}

This function generates a string with a literal array. Then you just eval() it.
It’s not as bad as it looks. The double backslashes makes the reading harder, but it’s simple.

Like cell reproduction, it first adds in one pass the most common things: the quotation marks, commas and opening parentheses, and then it adds the smaller details in two more passes. All of them run once for each level you specify (You could waste code in finding out the deepest level to start processing from, but a guess is enough.)

See the working demo / tool to convert tab-indented text into array

Or visit a php fiddle with all the passes, so you can expand on this.

Solution 4

There is a class on PHP Scripts that will do what you need. You can find it here:
Array To List

It takes a multidimensional array and creates the HTML.

Updated

The opposite to this was actually required. To do this, it’s probably best to use a DOMDocument object and load the HTML into a object representation.

http://php.net/manual/en/domdocument.loadhtml.php

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply