Recursively output a php array in nested div? - php

I have an array with a data structure like below
$array = array(
'someKey' => array(
'id' => 1,
'string' => 'some key',
'someKey2' => array(
'id' => 1,
'string' => 'some key two',
'someKeyThree' => array(
'id' => 1,
'string' => 'some key three',
,
),
),
'someOtherKey' => array(
),
);
What I would like to do is out every array as a nested div p structure,
<div>
<p>someKey</p> // key of first array value
<div>
<p>someKey2</p>
<div>
<p>SomeKeyThree</p>
</div>
</div>
</div>
I have tried using new RecursiveIteratorIterator(new RecursiveArrayIterator($this->getData(), RecursiveIteratorIterator::CHILD_FIRST));
and using that but I am having trouble as the end tag for div never ends up right. Also once the iterator reaches the bottom of an array with no array to go into I want it to stop iterating completely.
THanks

You have to call a function recursively.
function printUl($arr){
$output = "<ul>";
foreach ($arr as $key => $val){
if (is_array($val)){
$output .= printUl($val);
continue;
}
else{
$output .= "<li>".$val."</li>"
}
$output .= "</ul>";
return $output;
}
}

Related

How to loop through array with indeterminate amount of children arrays

Im trying to list categories with sub categories in my app - I can use either PHP or Javascript / Jquery for the following:
I have an array of categories with sub categories appended as additional arrays
the trick is that it can go as deep as there are sub categories.
and sub categories can also have sub categories.
Therefore for each category it can have as many children each of whom can have many children arrays.
What would be the best way to loop through them to create a dropdown list?
Here is the structure when dumping the main array:
array (size=2)
0 =>
array (size=4)
'id' => int 1
'name' => string 'Stationery' (length=10)
'parent_id' => int 0
'childs' =>
array (size=1)
0 =>
array (size=4)
...
1 =>
array (size=3)
'id' => int 4
'name' => string 'boots' (length=5)
'parent_id' => int 0
notice sub zero has a "childs" array
when dumping this array i get:
array (size=1)
0 =>
array (size=4)
'id' => int 2
'name' => string 'pens' (length=4)
'parent_id' => int 1
'childs' =>
array (size=1)
0 =>
array (size=4)
...
Notice this too has a child attached which when dumped looks like:
array (size=1)
0 =>
array (size=4)
'id' => int 3
'name' => string 'penfillers' (length=10)
'parent_id' => int 2
'childs' =>
array (size=1)
0 =>
array (size=3)
...
Sneaky - this one also has another child!
This can go as deep as there are sub categories
How would i loop through them and have the output in a dropdown list?
Im stumped as to how to loop infinitely until the chain ends.
Thanks
Jason
You should recursively yield all the options in the array. There are 2 ways to implement it. Depends on your PHP version.
To make the core logic cleaner, let's say we'd render the output with these utilities:
//
// some function to tidy up outputs
//
// simply make the recursive level visible
function pad_level($string, $level) {
// no pad for 0 level
if ($level == 0) return $string;
// pad some '-' to show levels
$pad = str_repeat('-', $level);
return $pad . ' ' . $string;
}
// render a leaf into standardized info array for an option
function option_from($item, $level) {
return array(
'value' => $item['id'],
'display_value' => pad_level($item['name'], $level),
);
}
// render options into HTML select
function render_select($name, $options) {
$output = '';
foreach ($options as $option) {
$output .= ' '.
'<option value="'.htmlspecialchars($option["value"]).'">'.
htmlspecialchars($option["display_value"]).
'</option>'."\n";
}
return '<select name="'.htmlspecialchars($name).'">'."\n".
$output."</select>\n";
}
// render options into plain text display
function render_plain_text($name, $options) {
$output = '';
foreach ($options as $option) {
$output .= $option["value"].' => '.$option["display_value"]."\n";
}
return $output;
}
These are the 2 methods:
//
// core logic
//
// Method 1: Generator. Available with PHP 5 >= 5.5.0
function options_in($array, $level=0) {
foreach ($array as $leaf) {
yield option_from($leaf, $level);
// yield the children options, if any
if (isset($leaf['childs']) && is_array($leaf['childs'])) {
foreach (options_in($leaf['childs'], $level+1) as $option) {
yield $option;
}
}
}
}
// Method 2: Normal recursion then merge arrays. For PHP 4 or after
function get_options($array, $level=0) {
$output = array();
// yield for the option array
foreach ($array as $leaf) {
$output[] = option_from($leaf, $level);
// yield the children
if (isset($leaf['childs']) && is_array($leaf['childs'])) {
$childs = get_options($leaf['childs'], $level+1);
$output = array_merge($output, $childs); // this could be slow
}
}
return $output;
}
And this is how you actually render some HTML from it:
// dummy input
$input = array(
array(
'id' => 1,
'name' => 'Stationery',
'parent_id' => 0,
'childs' => array(
array(
'id' => 2,
'name' => 'Pencil',
'parent_id' => 1,
),
array(
'id' => 3,
'name' => 'Pen',
'parent_id' => 1,
),
array(
'id' => 5,
'name' => 'Notepad',
'parent_id' => 1,
'childs' => array(
array(
'id' => 8,
'name' => 'Blue Pad',
'parent_id' => 3,
),
array(
'id' => 9,
'name' => 'Red Pad',
'parent_id' => 3,
),
array(
'id' => 10,
'name' => 'iPad',
'parent_id' => 3,
),
),
),
),
),
array(
'id' => 4,
'name' => 'boots',
'parent_id' => 0,
),
);
// method 1, preferred
echo "\nMethod 1\n";
echo render_select('mySelect', options_in($input));
echo render_plain_text('mySelect', options_in($input));
// method 2
echo "\nMethod 2\n";
echo render_select('mySelect', get_options($input));
echo render_plain_text('mySelect', get_options($input));
Here is a really simple example of how you could do it using recursion. I'm sure there is better ways but this is a very simple function so you can see the concept. The function calls itself until the job is done (I'm using the square bracket array notation here so make sure your PHP version supports it)
<?php
function getItems(array $items)
{
$return = [];
foreach ($items as $item) {
$return[] = $item;
if (isset($item['childs']) && count($item['childs']) > 0) {
$return = array_merge(
$return,
getItems($item['childs'])
);
}
}
return $return;
}
$array = [
0 => [
'id' => 1,
'childs' => []
],
1 => [
'id' => 2,
'childs' => [
0 => [
'id' => 3,
'childs' => []
]
]
]
];
print_r(getItems($array));
Then just loop over the results to create your select options. Hope this helps
create a sub function, input of the sub function would be object ,this function will check if it is an array or a simple element, if it is an array then it will call the function again on that element else return the element.
just elaborate "Gerald Schneider"'s answer.

Chunk doesn't see arguments

I have this snippet (I want to get elements from .xml file):
$movies = simplexml_load_file('http://www.example.com/example.xml');
$out = "";
foreach ($movies as $movie) {
$properties = array(
'photo' => $movie->image,
'title' => $movie->title,
'desc' => $movie->teaser,
'channel' => $movie->channel,
'date' => $movie->date);
$out .= $modx->getChunk('tpl_movies-item', $properties);
}
return $out;
And chunk tpl_movies-item:
<article>
[[+photo]]
[[+title]]
[[+desc]]
[[+date]]
[[+channel]]
aaa
</article>
It shows only "aaa" (but when I add "echo $properties['photo']" it prints right value), when I change values in array to strings, eg. 'desc' => "lololololol" it works right. Could u help me what should I do?
The problem is probably in your foreach statement since simplexml_load_file() needs ->children() to loop through the different childs of the object at hand. Try:
foreach ($movies->children() as $movie) {
$properties = array(
'photo' => (string)$movie->image,
'title' => (string)$movie->title,
'desc' => (string)$movie->teaser,
'channel' => (string)$movie->channel,
'date' => (string)$movie->date);
$out .= $modx->getChunk('tpl_movies-item', $properties);
}
UPDATE
Try casting the properties as string as in the example above since they could be returned as objects.

Laravel/PHP create an array from array

Hey guys I'm confused about how to create an array using specific keys from my pre-existing array.
Laravel controller
public function index()
{
$content = Page::find(1)->content->toArray();
return View::make('frontend.services', compact('content'));
}
$content is an array that looks similar to
array (
0 => array (
'id' => '1',
'page_id' => '1',
'name' => 'banner_heading',
'content' => 'some content', ),
1 => array (
'id' => '2',
'page_id' => '1',
'name' => 'banner_text',
'content' => 'some other content' )
)
And I want it recreate this array to look like this
array (
0 => array (
'banner_heading' => 'some content'
),
1 => array (
'banner_text' => 'some other content'
)
)
How can I move the keys name and content to equal their values as a single row in the array?
I greatly appreciate any advice.
PHP >= 5.5.0:
$result = array_column($content, 'content', 'name');
PHP < 5.5.0:
foreach($content as $key => $array) {
$result[$key] = array($array['name'] => $array['content']);
}
You mean
$newContent = array();
foreach ($content as $record) {
$newContent[] = array($record['name'] => $record['content']);
}
?
I don't know Laravel, but i believe that your solutions should be similar to this :
$newArray= array();
foreach($content as $key => $value)
{
$newArray[] = $value["banner_heading"];
}
return View::make('frontend.services', compact('newArray'));
Or at least it should be something similar with this.

Get path and value of all elements in nested associative array

Consider an associative array of arbitrary form and nesting depth, for example:
$someVar = array(
'name' => 'Dotan',
'age' => 35,
'children' => array(
0 => array(
'name' => 'Meirav',
'age' => 6,
),
1 => array(
'name' => 'Maayan',
'age' => 4,
)
),
'dogs' => array('Gili', 'Gipsy')
);
I would like to convert this to an associative array of paths and values:
$someVar = array(
'name' => 'Dotan',
'age' => 35,
'children/0/name' => 'Meirav',
'children/0/age' => 6,
'children/1/name' => 'Maayan',
'children/1/age' => 4,
'dogs/0' => 'Gili',
'dogs/1' => 'Gipsy'
);
I began writing a recursive function which for array elements would recurse and for non-array elements (int, floats, bools, and strings) return an array $return['path'] and $return['value']. This got sloppy quick! Is there a better way to do this in PHP? I would assume that callables and objects would not be passed in the array, though any solution which deals with that possibility would be best. Also, I am assuming that the input array would not have the / character in an element name, but accounting for that might be prudent! Note that the input array could be nested as deep as 8 or more levels deep!
Recursion is really the only way you'll be able to handle this, but here's a simple version to start with:
function nested_values($array, $path=""){
$output = array();
foreach($array as $key => $value) {
if(is_array($value)) {
$output = array_merge($output, nested_values($value, (!empty($path)) ? $path.$key."/" : $key."/"));
}
else $output[$path.$key] = $value;
}
return $output;
}
function getRecursive($path, $node) {
if (is_array($node)) {
$ret = '';
foreach($node as $key => $val)
$ret .= getRecursive($path.'.'.$key, $val);
return $ret;
}
return $path.' => '.$node."\n";
}
$r = getRecursive('', $someVar);
print_r($r);
All yours to place it in an array.

Know the element level in multidimensional array

Well, I am here again dealing with arrays in php. I need your hand to guide me in the right direction. Suppose the following array:
-fruits
--green
---limon
---mango
--red
---apple
-cars
--ferrari
---enzo
----blue
----black
---318
--lamborg
---spider
---gallardo
----gallado-96
-----blue
-----red
-----gallado-98
The - (hyphen) symbol only illustrates the deep level.
Well, I need to build another array (or whatever), because it should be printed as an HTML select as below:
-fruits
--green
---limon
---mango
--red
---apple
-cars
--ferrari
---enzo
----blue
----black
---318
--lamborg
---spider
---gallardo
----gallado-96
-----blue
-----red
-----gallado-98
Looks that for each level element, it should add a space, or hyphen to determinate that it belongs to a particular parent.
EDIT
The have provide an answer provideng my final code. The html select element will display each level as string (repeating the "-" at the begging of the text instead multi-level elements.
Here's a simple recursive function to build a select dropdown given an array. Unfortunately I'm not able to test it, but let me know if it works. Usage would be as follows:
function generateDropdown($array, $level = 1)
{
if ($level == 1)
{
$menu = '<select>';
}
foreach ($array as $a)
{
if (is_array($a))
{
$menu .= generateDropdown($a, $level+1);
}
else
{
$menu .= '<option>'.str_pad('',$level,'-').$a.'</option>'."\n";
}
}
if ($level == 1)
{
$menu = '</select>';
}
return $menu;
}
OK, I got it with the help of #jmgardhn2.
The data
This is my array:
$temp = array(
array(
'name' => 'fruits',
'sons' => array(
array(
'name' => 'green',
'sons' => array(
array(
'name' => 'mango'
),
array(
'name' => 'banana',
)
)
)
)
),
array(
'name' => 'cars',
'sons' => array(
array(
'name' => 'italy',
'sons' => array(
array(
'name' => 'ferrari',
'sons' => array(
array(
'name' => 'red'
),
array(
'name' => 'black'
),
)
),
array(
'name' => 'fiat',
)
)
),
array(
'name' => 'germany',
'sons' => array(
array(
'name' => 'bmw',
)
)
),
)
)
);
Recursive function
Now, the following function will provide an array with items like [level] => [name]:
function createSelect($tree, $items, $level)
{
foreach ($tree as $key)
{
if (is_array($key))
{
$items = createSelect($key, $items, $level + 1);
}
else
{
$items[] = array('level' => $level, 'text' => $key);
}
}
return $items;
}
Calling the funcion
Now, call the function as below:
$items = createSelect($temp, array(), 0);
Output
If you iterate the final $items array it will look like:
1fruits
2green
3mango
3banana
1cars
2italy
3ferrari
4red
4black
3fiat
2germany
3bmw

Categories