Convert Array of Paths into Ul List

Convert array of paths into UL list

Edit:

Changed to cater for updated question.

I'm using an array index of __title to hold the page title. As long as you never have a directory in your tree called __title this should be fine. You're free to change this sentinel value to anything you wish however.

I have also changed it so the list building function returns a string, so that you can store the value for use later in your page. (You can of course just do echo build_list(build_tree($paths)) to output the list directly.

<?php

$paths = array(
'about/contact/' => 'Contact Us',
'about/contact/form/' => 'Contact Form',
'about/history/' => 'Our History',
'about/staff/' => 'Our Staff',
'about/staff/bobjones/' => 'Bob',
'about/staff/sallymae/' => 'Sally',
'products/' => 'All Products',
'products/gifts/' => 'Gift Ideas!',
'products/widgets/' => 'Widgets'
);

function build_tree($path_list) {
$path_tree = array();
foreach ($path_list as $path => $title) {
$list = explode('/', trim($path, '/'));
$last_dir = &$path_tree;
foreach ($list as $dir) {
$last_dir =& $last_dir[$dir];
}
$last_dir['__title'] = $title;
}
return $path_tree;
}

function build_list($tree, $prefix = '') {
$ul = '';
foreach ($tree as $key => $value) {
$li = '';
if (is_array($value)) {
if (array_key_exists('__title', $value)) {
$li .= "$prefix$key/ <a href=\"/$prefix$key/\">${value['__title']}</a>";
} else {
$li .= "$prefix$key/";
}
$li .= build_list($value, "$prefix$key/");
$ul .= strlen($li) ? "<li>$li</li>" : '';
}
}
return strlen($ul) ? "<ul>$ul</ul>" : '';
}

$tree = build_tree($paths);
$list = build_list($tree);
echo $list;

?>

Convert paths into a nested unordered list using JavaScript:

Here's some vanilla JavaScript to do exactly what you specified.  I added a space between 'shop' and the list entry names to stop them from generating right up against each other.  Hope this helps!

let list  = document.querySelector('#list'); // get list
let items = list.querySelectorAll('[class*="item"]'); // get all items in list
let newList = document.createElement('ul');

items.forEach( item => {

const paths = item.innerText.split('/');
paths.splice(0,1); // remove first element since it's empty

let dir = newList;
let globalPath = "";

for( path of paths ){

globalPath = globalPath + '/' + path;

if( dir[path] == undefined ){

let subList = document.createElement('li'); // begin sublist
subList.innerText = path + ' '; // add space between the list entry and href

let href = document.createElement('a'); // begin href
href.href = globalPath;
subList.appendChild( href ); // end href

if( path != paths[paths.length-1] ){ // prep another ul to go deeper in the list
href.innerText = "shop";
let ul = document.createElement('ul');
subList.appendChild( ul );
dir[path] = ul;
}
dir.appendChild(subList); // end sublist

}

dir = dir[path]; // dive deeper

}

} );

list.innerHTML = newList.innerHTML; // replace old list with new list
<ul class="list" id="list">
<li class="item">/animal/dog/treats/biscuits</li>
<li class="item">/animal/dog/treats/cookies</li>
<li class="item">/clothes/shirts</li>
</ul>

Covert Array into ul li list

I have refactored the god-awful code in @hakre's answer to be slightly less god-aweful. It's using the same aglorithm:

$file = './CatDV/S1/SFX/steam/6004_90_04 LargeHeadlightSm.wav
./CatDV/S1/SFX/steam/AirHissPressureRe HIT032001.wav
./CatDV/S1/SFX/steam/Impact_Metal_Bullet_Hit(1).wav
./CatDV/S1/SFX/steam/Impact_Metal_Bullet_Hit.wav
./CatDV/S1/SFX/steam/6004_94_02 LargeHeavyGlassS.wav
./CatDV/S1/SFX/steam/Impact_Glass_Smash.wav
./CatDV/S1/SFX/steam/AirReleaseHeavyAi HIT030701.wav
./CatDV/S1/SFX/steam/SDR02_15SCIF RhythmicRever.wav
./CatDV/S1/SFX/steam/VAL02_08CRSH GlassBreaking.wav
./CatDV/S1/SFX/steam/AirReleaseLargeAi HIT030601.wav
./CatDV/S1/SFX/steam/SDR02_14SCIF Rhythmic3Beat.wav
./CatDV/S1/SFX/steam/6004_94_01 LargeGlassSmash.wav';

These are internal functions, which you don't need to directly call.

function splitIntoPaths($string) {
$paths = array();
foreach (explode("\n", $string) as $line) {
$node = &$paths;
foreach (explode("/", $line) as $segment) {
if (!isset($node[$segment])) {
$node[$segment] = array();
}
$node = &$node[$segment];
}
$node = $line;
}
return $paths;
}

function createListFromArray(array $array) {
$return = "<ul>";
foreach ($array as $key => $value) {
$return .= "<li>" . $key;
if (is_array($value)) {
$return .= createListFromArray($value);
}
$return .= "</li>";
}
return $return . "</ul>";
}

This is the function that does the work for you, returning you a string.

function createTree($string) {
$paths = splitIntoPaths($string);
return createListFromArray($paths);
}

Usage:

$tree = createTree($file);

Generating a nodes path from an unordered list

You could use parents(), to get the list of parents. You could take this further and get rid of the parent data attribute all together, which would simplify the following code.

$('#mybutton').on('click', function () {

var output = "";

$("ul#tree li").each(function () {
var id = $(this).data("id");
var parent = $(this).data("parent");
output = output + "id=" + id + "&parent=" + parent + "&node_path=";

var node_path = parent + ".";
$(this).parents('li').each(function() {
node_path += $(this).data("parent") + ".";
});
output += node_path.split('').reverse().join('')
output += "<br/>";
});

$("#mysection").html(output);

});

http://jsfiddle.net/4t6jgzhu/

PHP: Convert an array with a path attribute to a tree structure

Ok, I think I just found a solution:

function pathToTree($array){
$tree = array();
foreach($array AS $item) {
$pathIds = explode("/", ltrim($item["path"], "/") . $item["id"]);
$current = &$tree;
foreach($pathIds AS $id) {
if(!isset($current["childs"][$id])) $current["childs"][$id] = array();
$current = &$current["childs"][$id];
if($id == $item["id"]) {
$current = $item;
}
}
}
return $tree["childs"];
}

This is a dynamice solution for 1-n depth. Look at my example at http://ideone.com/gn0XLp . Here I tested it with some level:

  1. Continent
  2. Country
  3. City
  4. City-District
  5. City-Subdistrict
  6. City Sub-Sub-District

How to convert an array of paths into JSON structure?

const data = [
"/org/openbmc/examples/path0/PythonObj",
"/org/openbmc/UserManager/Group",
"/org/openbmc/HostIpmi/1",
"/org/openbmc/HostServices",
"/org/openbmc/UserManager/Users",
"/org/openbmc/records/events",
"/org/openbmc/examples/path1/SDBusObj",
"/org/openbmc/UserManager/User",
"/org/openbmc/examples/path0/SDBusObj",
"/org/openbmc/examples/path1/PythonObj",
"/org/openbmc/UserManager/Groups",
"/org/openbmc/NetworkManager/Interface"
];

const output = {};
let current;

for (const path of data) {
current = output;

for (const segment of path.split('/')) {
if (segment !== '') {
if (!(segment in current)) {
current[segment] = {};
}

current = current[segment];
}
}
}

console.log(output);

Your solution was close, you just didn't reset the current variable properly. Use this:

current = output;

instead of this:

current = output[path[0]];


Related Topics



Leave a reply



Submit