Better method to drill through array than nested loop? - php

I have a menu structure (Drupal) that contains elements representing a menu link. If the menu has a child menu (is the parent of a submenu) it has an array key 'below' with a menu item inside with the same structure. In theory this menu could be infinitely deep and the only way that I know how to go through each level is by create a new loop on the item if 'below' has anything in it. I know there has to be a more elegant and dynamic way to deal with this. I'm not really looking for a Drupal specific answer as this problem has come up before and I've just hacked it together. Thanks for reading!

Without an example of the kind of data you're working with it's difficult to give an exact implementation, but the general class of problems you describe is one that can usually be solved with recursion - a function that calls itself.
<?php
function deepPrintArr (array $arr)
{
$output = '';
foreach ($arr as $elem)
{
if (is_array $elem)
{
$output .= deepPrintArr ($elem);
}
else
{
$output .= "<p>$elem</p>\n";
}
}
return ($output);
}
echo (deepPrintArr ($some_deeply_nested_array));
?>

Please take time to "read" /inc/menu.inc in your drupal code.
This file contains a host of very usefull functions, some are not mentioned in any documentation.
menu_get_active_trail() for example gives you the current active path for the active page. This is a lot easier than looping recursive through all menu items in your own code.
For this site: Qrios I wrote some code to build a two level menu in Drupal:
<?php $base_tree = menu_tree_page_data($use_menu);
//Debug
//print_r($tree);
foreach ($base_tree as $item) {
if ($item['link']['in_active_trail'] == 1){
$tree = $item['below'];
}else{
$tree = array(); //empty array
}
} ?>
Not sure if this is what you mean, but you get the idea of using Drupals functions. You just have to find them.

Related

How to render a definition list from a nested array list with unknown nesting level

i am trying to render a FAQ-list from database data. The database resultset contains a list of arrays (top-level-categories) with each category contain either a set of Q+As following referred to as faqs or another set of categories following referred to as children.
I want to iterate over the resultset, and, when found a 'children'-element, render the following outer markup for the found category.
<div>
<section>category title</section>
<!-- In case this category has children, render this block here again
to show the sub-categories list under this category -->
<!-- In case this category has no children, but faqs, render the topics -->
</div>
At http://pastebin.com/czckiNUx i pasted how the data set to be iterated looks like.
I started with a couple nested foreach loops and found the nesting level to be potentially endless (because theoretically one could create nest as much sub-categories under a (sub-)category as wanted) and immediately wondered how to catch this case and render this theoretically unknown level of nesting.
I browsed this platform and read several topics incl. this one and tried to adapt the implementation but stuck with understanding the use of these Iterators. My experience with iterators is almost zero and when i browse the PHP manual i feel somewhat lost since i have no idea where to start or better how to stick these possibilities together to get a working implementation.
While i tried to adapt the solution from the linked topic i found that $iterator ignores all children- and faqs-elements which themselves are arrays and didn't understand why. It outputs only simple type data like strings and numbers. I don't get why and wonder how i have to implement it correctly.
It is required to evaluate every iterated element and check whether its the category title, the category description, the category id or the collection of sub-categories / faqs.
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($data));
foreach ($iterator as $key => $value)
{
if ($key == 'children')
{
// sub-categories found, find the faqs-elements and render the markup
// this element might contain further sub-categories (children-elements)
}
elseif ($key == 'faqs')
{
// collection of Q+As found ... iterate them and render the markup
// the iteration can't go any deeper
}
}
How do i have to implement this correctly?
Here's a function that will iterate through your data structure and print out information; you can adapt it as you see fit:
function iterate(&$array_of_aas)
{ // we have an array of associative arrays.
// $x is the associative array
foreach ($array_of_aas as $x)
{ echo "Found level " . $x['level'] . " with ID " . $x['id'] . "\n";
if(isset($x['children'])) {
// found some sub-categories! Iterate over them.
iterate($x['children']);
}
elseif (isset($x['faqs'])) {
echo "Found some FAQS!\n";
// collection of Q+As found ... iterate them and render the markup
// the iteration can't go any deeper
foreach ($x['faqs'] as $faq) {
echo 'ID: ' . $faq['id'] . ", category: " . $faq['catid'] . "\n" . $faq['title'] . "; " . $faq['description'] . "\n";
}
}
}
}

PHP function not running

My page grabs some records from a mssql DB, reads them into an array called $rows, with 3 columns:
ComputerName, Room, time_in_days
From $rows a second array is created (called $graph) with two columns, with the key being time_in_days from $rows, and the value column being a count of how often time_in_days occurred in the first array. The $graph array is used to create a number of HTML divs to give the impression of a graph.
I then want to be able to click on each individual div and using the $key value from the $graphs array, want to look up the rest of the information associated with all records in $rows where $graph[$key] matches $rows['time_in_days'] and display those records on the page.
But I have got stuck! I don't know where to put the function, the if statement(see code below) and at the moment, it doesn't even seem to be running the function. I don't even think I have the function code right. So if you could help with any of that I will very much appreciate it!
This is the code where the divs/graph is created:
foreach($graph as $key => $value){
$width = $value * $factor;
$page .= '<div style="width:40px;display:inline-block;">'.$key.'</div><div class="bar '.$key.'" style="width:'.$width.'px;"></div><div style="display:inline-block;margin-left:2px;">'.$value.'</div><br/>';
}
This is the function so far:
function SearchDays($key, $page, $rows) {
$computers = array();
foreach ($rows as $arr){
if ($key == $arr["time_in_days"]){
$computerName = $arr["ComputerName"];
$computers[$computerName] = $arr;
}
}
$page .= '<p>computers array from function SearchDays(): <pre>'.print_r($computers,true).'</pre></p>';
return;
}
And this is the if statement that makes the if statement that should run the function:
if (isset($_GET['barclick'])){
SearchDays($key, $page, $rows);
}
$page is just a variable that holds everything that is printed out onto the HTML page. The page itself can be seen here: cems.uwe.ac.uk/~s3-gupta/histogram/index.php. The whole page code can be got here: https://www.dropbox.com/s/h2q7x9xxtjbktx9/index.php
Thanks in advance. Please let me know if you need me to clarify things. I try and be as clear as possible on here, but usually don't manage it, so let me know if you need more.

Find directory structure in CodeIgniter

This is my CodeIgniter code to find the directory structure of a folder in my own server, but it is only going one level deep. I want to list all the subdirectories in the given $path. What is the error in this code?
function finddir($path)
{
$this->load->helper('directory');
$dir=directory_map($path,1);
//echo"$path";
foreach ($dir as $key => $subdir)
{
//echo $subdir."<br/>";
if(is_dir($subdir))
{
echo "<h3>$subdir</h3>";
$this->finddir($subdir);
}
else
{
echo "$subdir<br>";
}
}
}
The output goes only one level deep. Since I'm using recursion, I want it to go into deeper levels.
Try the RecursiveDirectoryIterator for this
function finddir($path)
{
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
echo "$name\n";
}
}
It makes more sense to make two functions so that you're not regenerating the array within a recursive function. This way it's generated once, and you are recursively getting values from just that one array. Unless the directory class is broken, you shouldn't need to check if it's a directory. If it's an array, it's a directory:
function finddir($path){
$this->load->helper('directory');
$dir=directory_map($path);
$this->recursive($dir);
}
function recursive($arr) {
foreach ($arr as $key => $val) {
if (is_array($val)){
echo "<h3>$key</h3>";
echo "<ul>\n";
$this->recursive($val);
echo "</ul>\n";
} else {
echo "<li>".$val."</li>\n";
}
}
}
Your break tags <br/> don't show the structure well, so I changed it to use nested lists.
I just noticed that you are pasing a value of 1 to the directory_map() function. That limits it to just one level, so you probably want to leave that out if you want to goes all the way with the recursion:
$dir=directory_map($path);
Why are you doint it this way?
The directory_map('source directory') returns you an array with sub-arrays (and sub-sub-arrays if applicable based on path).
You get the complete tree - just loop over array and print/use as needed, Use is_array($subdir) to test if its directory or file leaf.
instead of $dir=directory_map($path,1) remove number 1 so it displays this way $dir=directory_map($path) since that number will only return the first level directory only.

PHP array of numbers with range() and foreach loop

I have a really strange problem with range();
According to docs :
Create an array containing a range of elements
But when I do :
foreach (range(900,950,1) as $art_id){
//ob_start();
//do stuff
//do a lot more stuff
echo $art_id;
//ob_get_clean(); }
or even
$arts_id = range (900, 920);
foreach ($arts_id as $art_id){
//ob_start();
//do stuff
//do a lot more stuff
echo $art_id;
//ob_get_clean(); }
The output is strangly repeating itself in a series like
"900,900,901,900,901,902,900,901,9002,903,900..."
meaning it is comming back to the first ID after each loop.
(1st iteration -> 900
2nd iteration -> 900,901
3rd iteration -> 900,901,902
...)
When I just put a manual array it works perfectly in order and no duplicates :
$arts_id = array(900,901,902,903,904,905,906,907,908,909,910...);
What Am I doing wrong (again ?? )
EDIT I
here is the whole script :
http://pastebin.com/ZHm3ub6n
It is actually a slightly modified version of the slashdot scraping example included in the simplehtmldom script . Nothing special.
It is executed inside WP but OUTSIDE the loop ..
It must be in the rest of your code, because this works fine.
please share more of the script.
It looks like the foreach is nested within a similair foreach,
$arts_id = range (900, 920);
foreach ($arts_id as $art_id){
foreach (range (900,$art_id) as $art_id2){
echo $art_id2."<br/>";
}
}
This produces an output you've described
EDIT
Personally i'd add the the function scraping_slashdot a reset of the variable $ret just in case.
for example:
$ret = array();
Currently the echo of $output is within the loop, which creates an output like the following:
Article 1
Article 1, Article 2
Article 1, Article 2, Article 3
etc.
place echo $output outside the loop, or $ouptut = ''; inside the loop.

Convert foreach to for in PHP

foreach ( $this->parent->get_sections(null, $this->parent->author) as $section)
{
//...
}
I'm trying to do is force the loop to output each $section in the order I want. Each $section's name can be retrieved by $section->name. Let's say that I want to output $section "Section 2" first and then "Section 1" (and not in the order of the foreach). How can I force it do that? I presume the proper way would be a for loop with an if checking section names each time.
The proper way would be sorting the results when you call parent->get_sections(). How you would do this is entirely up to the implementation of that class and method. Changing this foreach to for for the sake of sorting seems like a code smell to me.
For the sake of answering the question as technical as possible.
$sections = $this->parent->get_sections(null, $this->parent->author);
$num_sections = count($sections);
for ($i = 0; $i < $num_sections; $i++) {
// what you do here is up to you $sections[$i]
}
Especially if you are not aware of the specific number of sections, you could use usort() to do a dynamic custom sort on the get_sections()-returned array or object and then utilize the existing code. (This is a little more elegant, imo, than doing the same in a for/foreach loop).
Not knowing the structure of your code, I would do something like.
// Get Org Sections
$sections = $this->parent->get_sections(null, $this->parent->author);
// Loop thru sections to get an array of names
foreach ( $sections as $key=>$section)
{
$sorted_sections[$section->name] = $key;
}
// Sort Array
//ksort — Sort an array by key
//krsort — Sort an array by key in reverse order
krsort($sorted_sections);
foreach ( $sorted_sections as $section)
{
// Orig Code
}
$section = $this->parent->get_sections(null, $this->parent->author);
echo $section[2]->name;
echo $section[1]->name;//just output the indexes the way you want
if you want it sorted, in say descending order, you can sort it that way and then use a for loop to display.

Categories