PHP function that creates a nested ul li?
This would be a excellent example of the use of recursion. An array (with sub-arrays within it) defines each level, and a function loops, calling itself whenever it finds a new array to process. As long as the function cleans up appropriately (closing the </li> & </ol>
), it's largely automatic.
<?php
// I know which function I'd rather write....
$tree = array('Projects (Menu Level 1)',
array('Project 1 (Menu Level 2)',
'Project 2 (Menu Level 2)',
'Project 3 (Menu Level 2)'),
'News (Menu Level 1)',
'Contact (Menu Level 1)');
// now quake beneath the power of this fully operational recursive function!
function olLiTree($tree)
{
echo '<ul>';
foreach($tree as $item) {
if (is_array($item)) {
olLiTree($item);
} else {
echo '<li>', $item, '</li>';
}
}
echo '</ul>';
}
olLiTree($tree); // kick off from the top level
php function that create nested ul li from array+object
This should get you started:
It uses a recursive function; it calls itself.
UPDATED
Tested with the JSON object and fixed some errors
<?php
echo make_ulli($object);
function make_ulli($array){
if(!is_array($array)) return '';
$output = '<ul>';
foreach($array as $item){
$output .= '<li>' . $item->t_name;
if(property_exists($item, 'childs'))
$output .= make_ulli($item->childs);
$output .= '</li>';
}
$output .= '</ul>';
return $output;
}
?>
Creating a nested UL from flat array in PHP
I would gather the data and construct a tree first, and then print the tree out. Some sample code:
foreach ($nodes as $item) {
// gather the data in an assoc array indexed by id
if ( !isset($data_by_id[ $item['top_id'] ])) {
$data_by_id[ $item['top_id'] ] = array( 'name' => $item['standard'] );
}
if ( !isset($data_by_id[ $item['parent_id'] ])) {
$data_by_id[ $item['parent_id'] ] = array( 'name' => $item['indicator'] );
}
if ( !isset($data_by_id[ $item['child_id'] ])) {
$data_by_id[ $item['child_id'] ] = array(
'name' => $item['element'],
'contents' => array(
$item['Connections'],
$item['Effective Practice'],
$item['Proficient'],
$item['Suggested Artifacts'])
);
}
// construct the tree - I've made a simple three tier array
$tree[ $item['top_id'] ][ $item['parent_id'] ][ $item['child_id'] ]++;
}
// go through the tree and print the info
// this is a recursive function that can be used on arbitrarily deep trees
function print_tree( $data, $arr ){
echo "<ul>\n";
// the tree is an associative array where $key is the ID of the node,
// and $value is either an array of child nodes or an integer.
foreach ($arr as $key => $value) {
echo "<li>" . $data[$key]['name'] . "</li>\n";
if (isset($data[$key]['contents']) && is_array($data[$key]['contents'])) {
echo '<ul>';
foreach ($data[$key]['contents'] as $leaf) {
echo '<li>' . $leaf . "</li>\n";
}
echo "</ul>\n";
}
// if $value is an array, it is a set of child nodes--i.e. another tree.
// we can pass that tree to our print_tree function.
if (is_array($value)) {
print_tree($data, $value);
}
}
echo "</ul>\n";
}
print_tree($data_by_id, $tree);
You'll need to add in error checking, zapping of strange characters, removal of excess whitespace, etc.
From php multidimensional array trying to create nested ul li menu (unlimited nested levels)
Here's a summary of what it does:
- flatten the array recursively
- build a multi-dimensional relation map
- create 1D relationships that link UpperLevelNumberRenamed to NumberRenamed
- print out the multi-dimensional as an ul-li list.
Here it is:
$flat = array();
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($main_topics)) as $i)
$flat[] = $i;
$final = array();
$defs = array();
for ($i = 0; $i < count($flat); $i += 3)
if ($flat[$i + 2] == 0) {
$final[$flat[$i + 1]] = array();
$defs[$flat[$i]] = &$final[$flat[$i + 1]];
} else {
$defs[$flat[$i + 2]][$flat[$i + 1]] = array();
$defs[$flat[$i]] = &$defs[$flat[$i + 2]][$flat[$i + 1]];
}
function array2ul($array) {
$out = "<ul>";
foreach($array as $key => $elem)
$out = is_array($elem) ?
$out . "<li><span>$key</span>" . array2ul($elem) . "</li>" :
$out = $out."<li><span>$key:[$elem]</span></li>";
$out = $out . "</ul>";
return $out;
}
echo array2ul($final);
Output:
<ul><li><span>Products</span><ul><li><span>Computers</span><ul><li><span>Laptops</span><ul><li><span>Dell</span><ul></ul></li><li><span>Acer</span><ul></ul></li></ul></li><li><span>Desktops</span><ul></ul></li></ul></li></ul></li><li><span>Home</span><ul></ul></li></ul>
PHP function generate UL LI
function olLiTree($tree) {
$out = '<ul>';
foreach($tree as $key => $value) {
$out.= '<li>';
if (is_array($value)) {
$out.= $key . olLiTree($value);
} else {
$out.= $value;
}
$out.= '</li>';
}
$out.= '</ul>';
return $out;
}
Always return
the final output, don't echo
it directly. You'll thank yourself one day when you find a situation where you don't want to echo
it immediately. :)
You might want to change the array structure to this, as it's less redundant (works with above code):
$tree = array('A', 'B', 'C' => array('CA', 'CB'), 'D');
php, nested ul/li list from list of strings
You can easily handle this using function and recursion, see example below:
$items = array ('page 1', 'page 2', 'page 3', 'page 4');
recursive($items);
function recursive($items)
{
if (is_array($items) && !empty($items)) {
echo "<ul><li>";
foreach ($items as $item) {
echo array_shift($items);
recursive($items);
break;
}
echo "</li></ul>";
}
}
Demo: https://eval.in/550199
Create nested array from unordered lists in files
Edit: OK, this was not so easy after all :)
By the way, this library is really an excellent tool. Kudos to the guys who wrote it.
Here is one possible solution:
class menu_parse {
static $missing = array(); // list of missing files
static private $files = array(); // list of source files to process
// initiate menu parsing
static function start ($file)
{
// start with root file
self::$files[$file] = 1;
// parse all source files
for ($res=array(); current(self::$files); next(self::$files))
{
// get next file name
$file = key(self::$files);
// parse the file
if (!file_exists ($file))
{
self::$missing[$file] = 1;
continue;
}
$html = file_get_html ($file);
// get menu root (if any)
$root = $html->find("ul[id=menu]",0);
if ($root) self::menu ($root, $res);
}
// reorder missing files array
self::$missing = array_keys (self::$missing);
// that's all folks
return $res;
}
// parse a menu at a given level
static private function menu ($menu, &$res)
{
foreach ($menu->children as $elem)
{
switch ($elem->tag)
{
case "li" : // name and possibly source file of a menu
// grab menu name
$name = $elem->plaintext;
// see if we can find a link to the menu file
$link = $elem->children(0);
if ($link && $link->tag == 'a')
{
// found the link
$file = $link->href;
$res[$name]->file = $file;
// add the source file to the processing list
self::$files[$file] = 1;
}
break;
case "ul" : // go down one level to grab items of the current menu
self::menu ($elem, $res[$name]->childs);
}
}
}
}
Usage:
// The result will be an array of menus indexed by item names.
//
// Each menu will be an object with 2 members
// - file -> source file of the menu
// - childs -> array of menu subtitems
//
$res = menu_parse::start ("root.html");
// parse_menu::$missing will contain all the missing files names
echo "Result : <pre>";
print_r ($res);
echo "</pre><br>missing files:<pre>";
print_r (menu_parse::$missing);
echo "</pre>";
Ouput of your test case:
Array
(
[Start] => stdClass Object
(
[childs] => Array
(
[Sub1] => stdClass Object
(
[file] => file1.html
[childs] => Array
(
[SubSub1] => stdClass Object
(
[file] => file3.html
[childs] => Array
(
[SubSubSub1] => stdClass Object
(
[file] => file7.html
)
[SubSubSub2] => stdClass Object
(
[file] => file8.html
)
[SubSubSub3] => stdClass Object
(
[file] => file9.html
)
)
)
[SubSub2] => stdClass Object
(
[file] => file3.html
)
[SubSub3] => stdClass Object
(
[file] => file5.html
)
[SubSub4] => stdClass Object
(
[file] => file6.html
)
)
)
[Sub2] => stdClass Object
(
[file] => file2.html
)
)
[file] => root.html
)
)
missing files: Array
(
[0] => file2.html
[1] => file5.html
[2] => file6.html
[3] => file7.html
[4] => file8.html
[5] => file9.html
)
Notes:
- The code assumes all item names are unique inside a given menu.
You could modify the code to have the (sub)menus as an array with numeric indexes and names as properties (so that two items with the same name would not overwrite each other), but that would complicate the structure of the result.
Should such name duplication occur, the best solution would be to rename one of the items, IMHO.
- The code also assume there is only one root menu.
It could be modified to handle more than one, but that does not make much sense IMHO (it would mean a root menu ID duplication, which would likely cause trouble to the JavaScript trying to process it in the first place).
PHP - Simple Nested Unordered List (UL) Array
This should do the trick:
$result = mysql_query("SELECT * FROM News");
$topicname = '';
// open list of topics
echo '<ul>';
// loop through topics
while($row = mysql_fetch_array($result)) {
if (!$row['TopicID']) {
// fake topic name for unsorted stuff
$row['TopicName'] = 'Sort Me';
}
if ($topicname != $row['TopicName']) {
if($topicname != ''){
// had a topic name, means we opened a list
// that hasn't been closed, close it.
echo '</ul>';
}
// print this topic and open the list of articles
echo '<li>' . $row['TopicName'] . '</li><ul>';
// update the current topic to be this TopicName
$topicname = $row['TopicName'];
}
// the news item
echo '<li>' . $row['NewsID'] . '"</li>';
}
if($topicname != ''){
// we saw at least one TopicName, we need to close
// the last open list.
echo '</ul>';
}
// end topic list
echo '</ul>';
I think your real problem is that you were opening two lists each time, but only closing one (even moving the last block inside the list).
For the second part of your (new) question:
I'll caveat that for larger lists (say, more than 300 items) the tradeoff I'm making with respect to storing the list in memory and iterating twice rather than just querying for counts needed would swing the other way. That is, the solution below puts everything into memory, then iterates a second time to print it out; an alternative would be to run two queries, one to find the number of unique TopicNames and one to find the number of total items in the list.
Also, for display, you really want to solve some optimization for layout, I'll do this naively and just make a (roughly) equal number of topics per column and when the division doesn't work out this will weight towards the left. You'll see where you can tweak or replace some code to get different (and better?) results.
$columns = // user specified;
$result = mysql_query("SELECT * FROM News");
$num_articles = 0;
// $dataset will contain array( 'Topic1' => array('News 1', 'News2'), ... )
$dataset = array();
while($row = mysql_fetch_array($result)) {
if (!$row['TopicID']) {
$row['TopicName'] = 'Sort Me';
}
$dataset[$row['TopicName']][] = $row['NewsID'];
$num_articles++;
}
$num_topics = count($dataset);
// naive topics to column allocation
$topics_per_column = ceil($num_topics / $columns);
$i = 0; // keeps track of number of topics printed
$c = 1; // keeps track of columns printed
foreach($dataset as $topic => $items){
if($i % $topics_per_columnn == 0){
if($i > 0){
echo '</ul></div>';
}
echo '<div class="Columns' . $columns . 'Group' . $c . '"><ul>';
$c++;
}
echo '<li>' . $topic . '</li>';
// this lists the articles under this topic
echo '<ul>';
foreach($items as $article){
echo '<li>' . $article . '</li>';
}
echo '</ul>';
$i++;
}
if($i > 0){
// saw at least one topic, need to close the list.
echo '</ul></div>';
}
Related Topics
Special Characters in "File_Exists" Problem (Php)
Function Eregi() Is Deprecated
How to Change the Woocommerce_Form_Field HTML Structure
Warning: MySQL_Result() Expects Parameter 1 to Be Resource, Boolean Given
Display Output in Parts in PHP
Long Integer Is Transformed When Inserted in Shorter Column, Not Truncated. Why? What Is the Formula
Round to Max Thousand, Hundred etc in PHP
Using Regex to Filter Attributes in Xpath with PHP
How to Put JavaScript Variable in PHP Echo
How to Create Codeigniter Language Files from Database
How to Avoid Code Repetition with PHP SQL Prepared Statements
Return Multiple Response Data in One Response
How to Set 777 Permission on a Particular Folder
PHP Object Attribute with Dot in Name