Deep array walk and output indentation - php

I have the following array structure, which is generated from a database:
Array
(
[0] => stdClass Object
(
[id] => 1
[parent] => 0
[children] => Array
(
[0] => stdClass Object
(
[id] => 2
[parent] => 1
[children] => Array
(
[0] => stdClass Object
(
[id] => 3
[parent] => 2
)
)
)
[1] => stdClass Object
(
[id] => 7
[parent] => 1
)
)
)
[1] => stdClass Object
(
[id] => 4
[parent] => 0
[children] => Array
(
[0] => stdClass Object
(
[id] => 5
[parent] => 4
[children] => Array
(
[0] => stdClass Object
(
[id] => 6
[parent] => 5
)
)
)
)
)
)
What I'd like to do is present this information in a HTML <select> box, with correct indentation to indicate the structure. So, for the given example, the result should look something like this:
- Select category
- Category 1
- Category 2
- Category 3
- Category 7
- Category 4
- Category 5
- Category 6
I am currently looping over the content using PHP's RecursiveIteratorIterator() class, which works to output all of them, but I can't figure out how to include the indentation. Here's the code I have now:
$html = '<select>';
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($tree), RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $i => $cat)
{
if($cat->id != '')
{
$option = new self($this->_db, $cat->id);
$html.= '<option value="'.$cat->id.'"';
$html.= '>'.$option->name;
$html.= '</option>';
}
}
$html.= '</select>';
return $html;
Can anyone point me in the right direction. Essentially, all I want to do is pad the option text by x spaces per nest depth.

If you are just wanting to pad the option name, you could use RecursiveIteratorIterator::getDepth and add the padding multiplied by the current depth:
$option_padding = str_repeat(" ", 4 * $cat->getDepth() );

Related

Best way to build multidimensional array (categories ; sub-categories; sub-sub-categories etc.)

Hello I have this response from my sql query :
Array
(
[0] => Array
(
[id] => 1
[name] => Category #1
[parent] => 0
)
[1] => Array
(
[id] => 2
[name] => Category #2
[parent] => 0
)
[2] => Array
(
[id] => 3
[name] => Category #3
[parent] => 0
)
[3] => Array
(
[id] => 4
[name] => Category #4
[parent] => 0
)
[4] => Array
(
[id] => 5
[name] => Sub category of category #1
[parent] => 1
)
[5] => Array
(
[id] => 6
[name] => Sub category of category #2
[parent] => 2
)
[6] => Array
(
[id] => 7
[name] => Sub category of category #6 (which is sub category of category #2)
[parent] => 6
)
)
After converting the response must looks like this :
array
(
array
(
[id] => 1
[name] => Category #1
[parent] => 0
[children]=> array
(
[id] => 5
[name] => Sub category of category #1
[parent] => 1
)
)
array
(
[id] => 2
[name] => За дома
[parent] => 0
[children]=> array
(
[id] => 6
[name] => Sub category of category #2
[parent] => 2
[children] => array
(
[id] => 7
[name] => Sub category of category #6 (which is sub category of category #2)
[parent] => 6
)
)
)
array
(
[id] => 3
[name] => Category #3
[parent] => 0
)
array
(
[id] => 4
[name] => Category #4
[parent] => 0
)
)
My question is - what is the best way to build such a multidimensional array (in php for example).
Try something like this
$arr = 'Your array';
$new = array();
foreach ($arr as $a){
$new[$a['parent']][] = $a;
}
$data = createMyTree($new, array($arr[0]));
print_r($data);
function createMyTree(&$list, $parent){
$tree = array();
foreach ($parent as $k=>$l){
if(isset($list[$l['id']])){
$l['children'] = createMyTree($list, $list[$l['id']]);
}
$tree[] = $l;
}
return $tree;
}
You can just store the data in a regular array and use code to traverse it back. So each node has its parentId in it, and as long as the parentId is not 0 you traverse back another parent till you get to the root parent of a given node.
you really do not need to think of it as a multi-dimensional array; which you will not be able to do any how because of the infinite dimensions. In other languages, such as C, you could just build a tree implementation (a classic data structure) using pointers. But in PHP i would just use a flat array, store the parentId, and use code to recurse.

Create a grouped array from another list of array items

I have an array of apps with ids and categories like this:
[apps] => Array
(
[0] => stdClass Object
(
[id] => 0
[categoryid] => 0
)
[1] => stdClass Object
(
[id] => 31265
[categoryid] => 12
)
[2] => stdClass Object
(
[id] => 15965
[categoryid] => 2
)
[3] => stdClass Object
(
[id] => 16554
[categoryid] => 12
)
)
I am trying to get all apps for a category based on this request. So, the resultant output for:
For CategoryId 12:
----------------
[apps] => Array
(
[0] => 31265
[1] => 16554
)
For CategoryId 2:
----------------
[apps] => Array
(
[0] => 15965
)
For CategoryId 0:
----------------
[apps] => Array
(
[0] => 0
)
I believe i need to use nested foreach loops, but is there an efficient method?
Thanks
You could cycle through them, and place them into category-arrays:
foreach ( $apps as $app ) {
$catArray[ $app[CategoryID] ][] = $app;
}
This should result in an array whose key represents a category, and whose nested arrays represent those apps in that category.
I've worked up a demo of this online at: http://codepad.org/WZXIvQ58

PHP Array parent/child display/inherit

I have an piece of my array here:
[0] => stdClass Object
(
[id] => 8005
[parentid] => 8004
[name] => a, b
[level] => 1
[prodtypename] => 1, 2, 3, 4
)
[1] => stdClass Object
(
[id] => 8006
[parentid] => 8005
[name] => c
[level] => 2
[prodtypename] =>
)
[2] => stdClass Object
(
[id] => 8007
[parentid] => 8005
[name] => d
[level] => 2
[prodtypename] =>
)
In my form I display them like:
<?php foreach ($this->myArray as $item): ?>
<?php echo $item->prodtypename ?>
it displays me current category and its current type. I need to display parent "prodtypename" next to child type, so we can know, what is parents types for current child category. Im trying to make display look something like this:
[1] => stdClass Object
(
[id] => 8006
[parentid] => 8005
[name] => c
[level] => 2
[prodtypename] => 1,2,3,4
)
Because parents "prodtypename" have 1.2.3.4, so the child inherits it. Thanks.
Based on the array you have already something like this might work assuming that you can rely on parents always appearing in the array before their children:
$prodTypeNames = array();
foreach ($this->myArray as $item) {
$prodTypeName = $item->prodtypename;
if( is_null( $item->prodtypename ) && isset( $prodTypeNames[$item->parentid] ) ) {
$prodTypeName = $prodTypeNames[$item->parentid];
}
$prodTypeNames[$item->id] = $prodTypeName;
echo $prodTypeName;
}
But you're probably better off getting the data in a better format to begin with.

Two Arrays, One Output (How to ForEach?)

Is there a way to foreach() through one array based on a matching value with a different key in another array? In this example, I have a category array ($cat_data) with cat_id as a key and an image array ($img_data) with category_id as a key.
Array (
[0] => Array (
[cat_id] => 1
[cat_name] => Category 1
)
[1] => Array (
[cat_id] => 2
[cat_name] => Category 2
)
)
Array (
[0] => Array (
[img_id] => 2
[img_name] => demo1.jpg
[img_label] => Demo 1
[category_id] => 2
[img_order] => 1
)
[1] => Array (
[img_id] => 3
[img_name] => demo2.jpg
[img_label] => Demo 2
[category_id] => 2
[img_order] => 2
)
[2] => Array (
[img_id] => 4
[img_name] => demo3.jpg
[img_label] => Demo 3
[category_id] => 1
[img_order] => 1
)
)
What I want is to output my display so it looks like the following:
Category 1
demo3.jpg
Category 2
demo1.jpg
demo2.jpg
Since I'm really not great at fully grasping arrays, I thought I'd try Stack, and I haven't been able to find an answer to my question, partially because I'm not sure what to ask for precisely. Any help??
The naïve way:
foreach ($cat_data as $cat) {
echo $cat['cat_name'];
foreach ($img_data as $img) {
if ($img['category_id'] != $cat['cat_id']) {
continue;
}
echo $img['img_name'];
}
}
This is rather inefficient, since it loops through the $imgs array several times, but easy and works.
More efficient:
$images = array();
foreach ($img_data as $img) {
$images[$img['category_id']][] = $img;
}
foreach ($cat_data as $cat) {
echo $cat['cat_name'];
if (isset($images[$cat['cat_id']])) {
foreach ($images[$cat['cat_id']] as $img) {
echo $img['img_name'];
}
}
}
This first groups all images by category into a new array, which you can then loop over directly once.
I would urge you to redesign your array when you fill them with data to instead look something like this.
Array (
[0] => Array (
[cat_id] => 1
[cat_name] => Category 1
[images] = Array(
[0] => Array (
[img_id] => 4
[img_name] => demo3.jpg
[img_label] => Demo 3
[category_id] => 1
[img_order] => 1
)
)
)
[1] => Array (
[cat_id] => 2
[cat_name] => Category 2
[images] = Array(
[0] => Array (
[img_id] => 4
[img_name] => demo3.jpg
[img_label] => Demo 3
[category_id] => 1
[img_order] => 1
)
[1] => Array (
[img_id] => 2
[img_name] => demo1.jpg
[img_label] => Demo 1
[category_id] => 2
[img_order] => 1
)
)
)
)
Then you would have all the relational data connected and would just have to loop through your array of categories and print the images associated with each one in turn. The numbered indexes could even be changed to associative names if the id of the catagory weren't important for example. Then the array could be indexed with the name of the category and just contain the images of that category.
If the images are to be used in other places where you initial layout of those fits better you could still use this layout for your main data graph. Just replace the actual data of the images in the images array under each category with a reference to the actual image object.

Recursively sort array to levels

I've been working on a site that uses binary mlm system.
Illustration here
So I have a two tables in database, users anad relationships. There is ID and personal data columns in users. Relationships has 4 columns: ID, parentID, childID, pos. Where pos is either left or right.
I have succesfully written a function that recursively lists all children of given pid (parentID). However I need to sort it in levels (for display and calculation purposes).
I have an array of children of user ID = 1:
Array
(
[0] => Array
(
[id] => 2
[parentID] => 1
[pos] => l
)
[1] => Array
(
[id] => 4
[parentID] => 2
[pos] => l
)
[2] => Array
(
[id] => 8
[parentID] => 4
[pos] => l
)
[3] => Array
(
[id] => 5
[parentID] => 2
[pos] => p
)
[4] => Array
(
[id] => 3
[parentID] => 1
[pos] => p
)
[5] => Array
(
[id] => 6
[parentID] => 3
[pos] => l
)
[6] => Array
(
[id] => 7
[parentID] => 3
[pos] => p
)
)
Now I have function named get_levels that returns an multidimensional array that should look like this:
Array
(
[0] => Array
(
[0] => Array
(
[id] => 2
[parentID] => 1
[pos] => l
)
[1] => Array
(
[id] => 3
[parentID] => 1
[pos] => p
)
)
[1] => Array
(
[0] => Array
(
[id] => 4
[parentID] => 2
[pos] => l
)
[1] => Array
(
[id] => 5
[parentID] => 2
[pos] => p
)
[2] => Array
(
[id] => 6
[parentID] => 3
[pos] => l
)
[3] => Array
(
[id] => 7
[parentID] => 3
[pos] => p
)
)
ETC.
)
Here's the function:
function get_levels($pid,$level, $level_id){
$children = children_array($pid,1);
if (sizeof($children) > 0):
foreach ($children as $child):
if ($child["parentID"] == $pid):
get_levels($child["id"], $level, $level_id+1);
$level[$level_id][] = $child;
endif;
endforeach;
endif;
return $level;
}
function children_array($pid, $depth) returns the children ... for $depth = 1 it returns immediate children (0 or 1 or 2), for $depth = 0 it returns all children
Can anyone help me with this function? I think the function works, however I don't know how to recursively use and add to array.
Looks like you're using a data structure within the wrong context. It's a binary tree, yet it's represented into a multilevel array which in short doesn't define its boundaries and rules of use.
When using the tree, I would use something like a Node class that has two children, left and right. Iterating through the tree would be piece of cake, inserting/deleting/editing into it is easily done depending on which set of rules you want to follow. When storing the tree, I would use some kind of Ahnentafel list which can easily be done in a relational database.
I would in no way mix both iteration and storage processes because if I change the rules of storage, I might also have to change the rules of iteration and vice versa.

Categories