Viewed   106 times

I have an array in PHP, which looks like this:

array (
    [0] => array (
        [id] => 1
        [title] => "Title 1"
        [parent_id] => NULL
        [depth] => 0
    )
    [1] => array (
        [id] => 2
        [title] => "Title 2"
        [parent_id] => NULL
        [depth] => 0
    )
    [2] => array (
        [id] => 3
        [title] => "Title 3"
        [parent_id] => 2
        [depth] => 1
    )
    [3] => array (
        [id] => 4
        [title] => "Title 4"
        [parent_id] => 2
        [depth] => 1
    )
    [4] => array (
        [id] => 5
        [title] => "Title 5"
        [parent_id] => NULL
        [depth] => 0
    )
    [5] => array (
        [id] => 6
        [title] => "Title 6"
        [parent_id] => 4
        [depth] => 2
    )
)

What i want to do is iterate over this array and create a nested <ol> list from it. So the result should look like this:

<ol>
    <li>Title 1</li> // id = 1
    <li>Title 2</li> // id = 2
    <ol>
        <li>Title 3</li> // id = 3 -> parent_id = 2
        <li>Title 4</li> // id = 4 -> parent_id = 2
        <ol>
            <li>Title 6</li> // id = 6 -> parent_id = 4
        </ol>
    </ol>
    <li>Title 5</li> // id = 5
</ol>

I've been trying to think of a way how i could get this done. But so far every attempt failed...

Anyone any idea how i can create such a nested <ol> list from an array like that?

Please note that i do not have any control on the given data. I simply make a call to an API and it returns json data, which i convert to an array. And the array looks exactly like the one i described.

 Answers

3

You should use recursion:

First the array in 'php' syntax:

<?php
$a=array (
    '0' => array (
        'id' => 1,
        'title' => "Title 1",
        'parent_id' => 'NULL',
        'depth' => 0
    ),
    '1' => array (
        'id' => 2,
        'title' => "Title 2",
        'parent_id' => 'NULL',
        'depth' => 0
    ),
    '2' => array (
        'id' => 3,
        'title' => "Title 3",
        'parent_id' => 2,
        'depth' => 1
    ),
    '3' => array (
        'id' => 4,
        'title' => "Title 4",
        'parent_id' => 2,
        'depth' => 1
    ),
    '4' => array (
        'id' => 5,
        'title' => "Title 5",
        'parent_id' => 'NULL',
        'depth' => 0
    ),
    '5' => array (
        'id' => 6,
        'title' => "Title 6",
        'parent_id' => 4,
        'depth' => 0
    )
);

Here the code:

$level = 'NULL';

function r( $a, $level) {
   $r = "<ol>";
   foreach ( $a as $i ) {
       if ($i['parent_id'] == $level ) {
          $r = $r . "<li>" . $i['title'] . r( $a, $i['id'] ) . "</li>";
       }
   }
   $r = $r . "</ol>";
   return $r;
}

print r( $a, $level );

?>

The results:

<ol><li>Title 1<ol></ol></li><li>Title 2<ol><li>Title 3<ol>
</ol></li><li>Title 4<ol><li>Title 6<ol></ol></li></ol></li></ol></li><li>Title 5
<ol></ol></li></ol>
  1. Title 1n
    1. Title 2n
      1. Title 3n
        1. Title 4n
          1. Title 6n
        2. Title 5n

          EDITED AFTER CHECK AS SOLUTION

          To avoid empty leafs:

          function r( $a, $level) {
             $r = '' ;
             foreach ( $a as $i ) {
                 if ($i['parent_id'] == $level ) {
                    $r = $r . "<li>" . $i['title'] . r( $a, $i['id'] ) . "</li>";
                 }
             }
             return ($r==''?'':"<ol>". $r . "</ol>");
          }
          
          Saturday, November 5, 2022
          1

          Actually, this can be done. Through a php extension.

          File: config.m4

          PHP_ARG_ENABLE(test, whether to enable test Extension support, [ --enable-test   Enable test ext support])
          
          if test "$PHP_TEST" = "yes"; then
            AC_DEFINE(HAVE_TEST, 1, [Enable TEST Extension])
            PHP_NEW_EXTENSION(test, test.c, $ext_shared)
          fi
          

          File: php_test.h

          #ifndef PHP_TEST_H
          #define PHP_TEST_H 1
          
          #define PHP_TEST_EXT_VERSION "1.0"
          #define PHP_TEST_EXT_EXTNAME "test"
          
          PHP_FUNCTION(getaddress4);
          PHP_FUNCTION(getaddress);
          
          extern zend_module_entry test_module_entry;
          #define phpext_test_ptr &test_module_entry
          
          #endif
          

          File: test.c

          #ifdef HAVE_CONFIG_H
          #include "config.h"
          #endif
          
          #include "php.h"
          #include "php_test.h"
          
          ZEND_BEGIN_ARG_INFO_EX(func_args, 1, 0, 0)
          ZEND_END_ARG_INFO()
          
          static function_entry test_functions[] = {
              PHP_FE(getaddress4, func_args)
              PHP_FE(getaddress, func_args)
              {NULL, NULL, NULL}
          };
          
          zend_module_entry test_module_entry = {
          #if ZEND_MODULE_API_NO >= 20010901
              STANDARD_MODULE_HEADER,
          #endif
              PHP_TEST_EXT_EXTNAME,
              test_functions,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL,
          #if ZEND_MODULE_API_NO >= 20010901
              PHP_TEST_EXT_VERSION,
          #endif
              STANDARD_MODULE_PROPERTIES
          };
          
          #ifdef COMPILE_DL_TEST
          ZEND_GET_MODULE(test)
          #endif
          
          PHP_FUNCTION(getaddress4)
          {
              zval *var1;
              zval *var2;
              zval *var3;
              zval *var4;
              char r[500];
              if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aaaa", &var1, &var2, &var3, &var4) == FAILURE ) {
                RETURN_NULL();
              }
              sprintf(r, "n%p - %p - %p - %pn%p - %p - %p - %p", var1, var2, var3, var4, Z_ARRVAL_P(var1), Z_ARRVAL_P(var2), Z_ARRVAL_P(var3), Z_ARRVAL_P(var4) );
              RETURN_STRING(r, 1);
          }
          
          PHP_FUNCTION(getaddress)
          {
              zval *var;
              char r[100];
              if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &var) == FAILURE ) {
                RETURN_NULL();
              }
              sprintf(r, "%p", Z_ARRVAL_P(var));
              RETURN_STRING(r, 1);
          }
          

          Then all you have to do is phpize it, config it, and make it. Add a "extension=/path/to/so/file/modules/test.so" to your php.ini file. And finally, restart the web server, just in case.

          <?php
            $x = array("123"=>"123");
            $w = $x;
            $y = $x;
            $z = &$x;
            var_dump(getaddress4($w,$x,$y,$z));
            var_dump(getaddress($w));
            var_dump(getaddress($x));
            var_dump(getaddress($y));
            var_dump(getaddress($z));
          ?>
          

          Returns(at least for me, your memory addresses will probably be different)

          string '
          0x9efeb0 - 0x9effe0 - 0x9ef8c0 - 0x9efeb0
          0x9efee0 - 0x9f0010 - 0x9ed790 - 0x9efee0' (length=84)
          
          string '0x9efee0' (length=8)
          
          string '0x9f0010' (length=8)
          
          string '0x9ed790' (length=8)
          
          string '0x9efee0' (length=8)
          

          Thanks to Artefacto for pointing this out, but my original code was passing the arrays by value, so thereby was recreating arrays including the referenced-one, and giving you bad memory values. I have since changed the code to force all params to be passed by reference. This will allow references, arrays, and object, to be passed in unmolested by the php engine. $w/$z are the same thing, but $w/$x/$y are not. The old code, actually showed the reference breakage and the fact that the memory addresses would change or match when all variables were passed in vs multiple calls to the same function. This was because PHP would reuse the same memory when doing multiple calls. Comparing the results of the original function would be useless. The new code should fix this problem.

          FYI - I'm using php 5.3.2.

          Sunday, September 4, 2022
           
          sk0x50
           
          3

          You can try below code to merge array. Code generates desired output required to you. I have used sample array as given by you:

          <?php
              $arr1=array(
                  "384"=>array("name"=>"SomeMovieName1","age"=>"12.2 hrs","IMDBLink"=>"","IMDBRating"=>"", "coverArt"=>""),
                  "452"=>array("name"=>"SomeMovieName2","age"=>"15.2 hrs","IMDBLink"=>"","IMDBRating"=>"", "coverArt"=>""),
                  "954"=>array("name"=>"SomeMovieName3","age"=>"4.2 hrs","IMDBLink"=>"","IMDBRating"=>"", "coverArt"=>"")
              );
              $arr2=array(
                 "384" => array("IMDBLink" => "7.2", "IMDBRating" => "http://www.imdb.com/LinkToMovie1", "coverArt" => "http://www.SomeLinkToCoverArt.com/1"),
                 "452" => array("IMDBLink" => "5","IMDBRating" => "http://www.imdb.com/LinkToMovie2", "coverArt" => "http://www.SomeLinkToCoverArt.com/2"),
                 "954"=>array("IMDBLink" => "8","IMDBRating" => "http://www.imdb.com/LinkToMovie3", "coverArt" => "http://www.SomeLinkToCoverArt.com/3")
              );
              $arr3 = array();
              foreach($arr1 as $key=>$val)
              {
                   $arr3[] = array_merge($val, $arr2[$key]);
              }
              echo "<pre>";
              print_r($arr3);
          ?>
          
          Tuesday, September 13, 2022
          3

          Try this;

          function buildTree(Array $data, $parent = 0) {
              $tree = array();
              foreach ($data as $d) {
                  if ($d['parent'] == $parent) {
                      $children = buildTree($data, $d['id']);
                      // set a trivial key
                      if (!empty($children)) {
                          $d['_children'] = $children;
                      }
                      $tree[] = $d;
                  }
              }
              return $tree;
          }
          
          
          $rows = array(
              array ('id' => 1, 'name' => 'Test 1', 'parent' => 0),
              array ('id' => 2, 'name' => 'Test 1.1', 'parent' => 1),
              array ('id' => 3, 'name' => 'Test 1.2', 'parent' => 1),
              array ('id' => 4, 'name' => 'Test 1.2.1', 'parent' => 3),
              array ('id' => 5, 'name' => 'Test 1.2.2', 'parent' => 3),
              array ('id' => 6, 'name' => 'Test 1.2.2.1', 'parent' => 5),
              array ('id' => 7, 'name' => 'Test 2', 'parent' => 0),
              array ('id' => 8, 'name' => 'Test 2.1', 'parent' => 7),
          );
          
          $tree = buildTree($rows);
          // print_r($tree);
          
          function printTree($tree, $r = 0, $p = null) {
              foreach ($tree as $i => $t) {
                  $dash = ($t['parent'] == 0) ? '' : str_repeat('-', $r) .' ';
                  printf("t<option value='%d'>%s%s</option>n", $t['id'], $dash, $t['name']);
                  if ($t['parent'] == $p) {
                      // reset $r
                      $r = 0;
                  }
                  if (isset($t['_children'])) {
                      printTree($t['_children'], ++$r, $t['parent']);
                  }
              }
          }
          
          
          print("<select>n");
          printTree($tree);
          print("</select>");
          

          Output;

          <select>
              <option value='1'>Test 1</option>
              <option value='2'>- Test 1.1</option>
              <option value='3'>- Test 1.2</option>
              <option value='4'>-- Test 1.2.1</option>
              <option value='5'>-- Test 1.2.2</option>
              <option value='6'>--- Test 1.2.2.1</option>
              <option value='7'>Test 2</option>
              <option value='8'>- Test 2.1</option>
          </select>
          

          And in your case;

          <select>
              <option value='1'>Baden-Württemberg</option>
              <option value='2'>- DMP-Verträge</option>
              <option value='50'>- Sprechstundenbedarf</option>
              <option value='52'>- Richtgrößen</option>
              <option value='53'>- Prüfungen</option>
              <option value='54'>- DMP-Verträge</option>
              <option value='55'>- Sonstige Verträge</option>
              <option value='3'>Berlin</option>
              <option value='62'>- DMP-Verträge</option>
              <option value='63'>- Prüfungen</option>
              <option value='64'>- Richtgrößen</option>
              <option value='65'>- Sonstige Verträge</option>
              <option value='66'>- Sprechstundenbedarf</option>
              <option value='4'>Brandenburg</option>
              <option value='67'>- DMP-Verträge</option>
              <option value='68'>- Prüfungen</option>
              <option value='69'>- Richtgrößen</option>
              <option value='70'>- Sonstige Verträge</option>
              <option value='71'>- Sprechstundenbedarf</option>
              <option value='5'>Bremen</option>
              <option value='72'>- DMP-Verträge</option>
              <option value='73'>- Prüfungen</option>
              <option value='74'>- Richtgrößen</option>
              <option value='75'>- Sonstige Verträge</option>
              <option value='76'>- Sprechstundenbedarf</option>
              <option value='7'>Hessen</option>
              <option value='6'>Hamburg</option>
              <option value='8'>Mecklenburg-Vorpommern</option>
              <option value='9'>Niedersachsen</option>
              <option value='10'>Nordrhein</option>
              <option value='11'>Rheinland-Pfalz</option>
              <option value='12'>Saarland</option>
              <option value='13'>Sachsen</option>
              <option value='14'>Sachsen-Anhalt</option>
              <option value='15'>Schleswig-Holstein</option>
              <option value='16'>Thüringen</option>
              <option value='17'>Westfalen-Lippe</option>
              <option value='51'>Richtgrössen</option>
              <option value='56'>Bayern</option>
              <option value='57'>- DMP-Verträge</option>
              <option value='58'>- Prüfungen</option>
              <option value='59'>- Richtgrößen</option>
              <option value='60'>- Sonstige Verträge</option>
              <option value='61'>- Sprechstundenbedarf</option>
          </select>
          
          Monday, December 5, 2022
          2

          This is the way that someone suggested to solved for String type. Cast2(List<?>) returns the multidimensional array. It may be generalized to use the class type as parameter. Thank you for your comments.

          static int dimension2(Object object) {
          
              int result = 0;
              if (object instanceof List<?>) {
          
                  result++;
                  List<?> list = (List<?>) object;
                  for (Object element : list) {
                      if (element != null) {
                          result += dimension2(element);
                          break;
                      }
                  }
              }
          
              return result;
          }
          
          
          static Object cast2(List<?> l) {
          
              int dim = dimension2(l);
              if (dim == 1) {
                  return l.toArray(new String[0]);
              }
          
              int[] dims = new int[dimension2(l)];
              dims[0] = l.size();
              Object a = Array.newInstance(String.class, dims);
              for (int i = 0; i < l.size(); i++) {
          
                  List<?> e = (List<?>) l.get(i);
                  if (e == null) {
                      Array.set(a, i, null);
                  } else if (dimension2(e) > 1) {
                      Array.set(a, i, cast2(e));
                  } else {
                      Array.set(a, i, e.toArray(new String[0]));
                  }
              }
              return a;
          }
          
          Tuesday, December 13, 2022
           
          Only authorized users can answer the search term. Please sign in first, or register a free account.
          Not the answer you're looking for? Browse other questions tagged :