PHP: Formatting multi-dimensional array as HTML? - php

I have tried to get my head around building a recursive function to handle formatting of a unknown depth multi-dimensional array to HTML and nested Divs. I thought that it should be a piece of cake, but no.
Here's what I have come up with this far:
function formatHtml($array) {
$var = '<div>';
foreach ($array as $k => $v) {
if (is_array($v['children']) && !empty($v['children'])) {
formatHtml($v['children']);
}
else {
$var .= $v['cid'];
}
}
$var.= '</div>';
return $var;
}
And here's my array:
Array
(
[1] => Array
(
[cid] => 1
[_parent] =>
[id] => 1
[name] => 'Root category'
[children] => Array
(
[2] => Array
(
[cid] => 2
[_parent] => 1
[id] => 3
[name] => 'Child category'
[children] => Array ()
)
)
)
)

You're missing only one important piece: when you make the recursive call to formatHtml() you're not actually including the returned content anywhere! Append it to $var and you should get much better results:
function formatHtml($array) {
$var = '<div>';
foreach ($array as $k => $v) {
if (is_array($v['children']) && !empty($v['children'])) {
$var .= formatHtml($v['children']);
}
else {
$var .= $v['cid'];
}
}
$var.= '</div>';
return $var;
}

Related

How to parse nested Array with Laravel

Hey guys i am new of the laravel, how can i parse this output array? This is my array is coming from repeater using jquery.
Array
(
[tour_baslik] => 1. Day
[tour_icerik] => content here....
[lunch] => Array
(
[0] => 2
)
[dinner] => Array
(
[0] => 1
[1] => 2
)
)
Array
(
[tour_baslik] => 2.Day
[tour_icerik] => content 2 here...
[lunch] => Array
(
[0] => 1
[1] => 2
)
[dinner] => Array
(
[0] => 2
)
)
I need parse like that but i'm stuck:
foreach($myarray as $key => $data){
echo $key . '-' . $data; }
Output must be:
tour_baslik - 1.day
tour_icerik - content here..
lunch - 2
dinner - 1,2
If you need to iterate through all items of your input, you could use a recursive function like the following:
function iterator($key, $value) {
if (is_array($value)) {
foreach ($value as $key => $value) {
iterator($key, $value);
}
} else {
echo !empty($key) ? "$key - " : "";
echo $value."\n";
}
}
iterator(null, $input);

Recursive Multidimensional Array to HTML nested code

Im trying to create nested blocks of HTML code from a Multidimensional Array in PHP using a recursive function. However I can not seem to print the tags in a nested order.
Here is the original array:
$array_1 =
array
(
array
(
'id' => '1',
'tag' => 'div1',
'parent' => '0'
),
array
(
'id' => '2',
'tag' => 'div2',
'parent' => '1'
),
array
(
'id' => '3',
'tag' => 'div3',
'parent' => '2'
),
array
(
'id' => '4',
'tag' => 'div4',
'parent' => '2'
),
array
(
'id' => '5',
'tag' => 'div5',
'parent' => '0'
),
array
(
'id' => '6',
'tag' => 'div6',
'parent' => '5'
),
array
(
'id' => '7',
'tag' => 'div7',
'parent' => '0'
)
);
The first thing I do is to use a function to turn this array into a multidimensional array by building a tree structure using the parent element of each record as reference:
function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element)
{
if ($element['parent'] == $parentId)
{
$children = buildTree($elements, $element['id']);
if ($children)
{
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
$tree_1 = buildTree($array_1);
Once this is done the multidimensional array should look like this:
Array
(
[0] => Array
(
[id] => 1
[name] => div1
[parent] => 0
[children] => Array
(
[0] => Array
(
[id] => 2
[name] => div2
[parent] => 1
[children] => Array
(
[0] => Array
(
[id] => 3
[name] => div3
[parent] => 2
)
[1] => Array
(
[id] => 4
[name] => div4
[parent] => 2
)
)
)
)
)
[1] => Array
(
[id] => 5
[name] => div5
[parent] => 0
[children] => Array
(
[0] => Array
(
[id] => 6
[name] => div6
[parent] => 5
)
)
)
[2] => Array
(
[id] => 7
[name] => div7
[parent] => 0
)
)
The next thing to do is to output the html elements in a nested order. In this example Im using symbolic tag names such as div1, div2, etc; but in reality they could be any html tags like divs, h1, ul,li, form, etc; with their opening and closing tags.
To accomplish this I use the following recursive function, however my problem is that it does not show the html elements in a nested order as they should be.
function recursive($array, $level = 0)
{
foreach($array as $key => $value)
{
if (!is_array($value) && $key == "tag") { echo str_repeat(" ", $level), "[".$value."]", ''; }
//If $value is an array.
if(is_array($value))
{
//We need to loop through it.
recursive($value, $level + 1);
}
else
{
//It is not an array, so print it out.
if ($key == "tag") { echo "[/".$value."]", '<br>'; }
}
}
}
$tree_2 = recursive($tree_1);
echo $tree_2;
This is how it currently outputs the html tags from the array:
[div1][/div1]
[div2][/div2]
[div3][/div3]
[div4][/div4]
[div5][/div5]
[div6][/div6]
[div7][/div7]
And this is how the html tags should be output in reality based on the multidimensional array's nested order (please note how some div tags contain other div tags before their closing element):
[div1]
[div2]
[div3][/div3]
[div4][/div4]
[/div2]
[/div1]
[div5]
[div6][/div6]
[/div5]
[div7][/div7]
How can I get the html tags printed in the right nested order like in the last example? What am I missing? Thanks so much!
The issue is that the key tag is before children in the $tree_1 array.
Whilst I would personally change your data structure around to make it "easier" to use in the recursive function, you can change your recursive function to make it work like this:
function recursive($array, $level = 0)
{
// Check if we are using an associative array or indexed array
if(isset($array['tag'])) {
echo str_repeat(" ", $level), "[" . $array['tag'] . "]", '';
if(isset($array['children'])) {
echo "<br />";
recursive($array['children'], $level + 1);
echo str_repeat(" ", $level), "[/" . $array['tag'] . "]" , '';
} else {
echo "[/" . $array['tag'] . "]";
}
echo "<br />";
} else {
foreach($array as $key => $value)
{
recursive($value, $level + 1);
}
}
}
Add additional line breaks and formatting where required
A rewrite of your function:
function recursive2($array, &$level, $indentClosingTag = true)
{
$level++;
for($i = 0; $i<count($array); $i++) {
$data = $array[$i];
$indent = str_repeat(' ', $level);
echo $indent . "[".$data['tag'].']';
if (isset($data['children'])) {
echo '<br>';
$level++;
recursive2($data['children'], $level, true);
$level--;
} else {
$indentClosingTag = false;
}
echo ($indentClosingTag ? $indent : ''). "[/".$data['tag'].']'.'<br>';
}
$level--;
}
$l = 0;
recursive2($tree_1, $l);

Sort array values based on parent/child relationship

I am trying to sort an array to ensure that the parent of any item always exists before it in the array. For example:
Array
(
[0] => Array
(
[0] => 207306
[1] => Bob
[2] =>
)
[1] => Array
(
[0] => 199730
[1] => Sam
[2] => 199714
)
[2] => Array
(
[0] => 199728
[1] => Simon
[2] => 207306
)
[3] => Array
(
[0] => 199714
[1] => John
[2] => 207306
)
[4] => Array
(
[0] => 199716
[1] => Tom
[2] => 199718
)
[5] => Array
(
[0] => 199718
[1] => Phillip
[2] => 207306
)
[6] => Array
(
[0] => 199720
[1] => James
[2] => 207306
)
)
In the above array this "fails" as [1][2] (Sam) does not yet exist and nor does [4][2] (Tom).
The correct output would be as, in this case, as both Sam and Tom's parents already exist before they appear in the array:
Array
(
[0] => Array
(
[0] => 207306
[1] => Bob
[2] =>
)
[1] => Array
(
[0] => 199714
[1] => John
[2] => 207306
)
[2] => Array
(
[0] => 199730
[1] => Sam
[2] => 199714
)
[3] => Array
(
[0] => 199728
[1] => Simon
[2] => 207306
)
[4] => Array
(
[0] => 199718
[1] => Phillip
[2] => 207306
)
[5] => Array
(
[0] => 199716
[1] => Tom
[2] => 199718
)
[6] => Array
(
[0] => 199720
[1] => James
[2] => 207306
)
)
I found an answer https://stackoverflow.com/a/12961400/1278201 which was very close but it only seems to go one level deep (i.e. there is only ever one parent) whereas in my case there could be 1 or 10 levels deep in the hierarchy.
How do I sort the array so no value can appear unless its parent already exists before it?
This will trivially order the array (in O(n)) putting first all those with no parent, then these whose parent is already in the array, iteratively, until there's no children having the current element as parent.
# map the children by parent
$parents = ['' => []];
foreach ($array as $val) {
$parents[$val[2]][] = $val;
}
# start with those with no parent
$sorted = $parents[''];
# add the children the current nodes are parent of until the array is empty
foreach ($sorted as &$val) {
if (isset($parents[$val[0]])) {
foreach ($parents[$val[0]] as $next) {
$sorted[] = $next;
}
}
}
This code requires PHP 7, it may not work in some cases under PHP 5. - for PHP 5 compatibility you will have to swap the foreach ($sorted as &$val) with for ($val = reset($sorted); $val; $val = next($sorted)):
# a bit slower loop which works in all versions
for ($val = reset($sorted); $val; $val = next($sorted)) {
if (isset($parents[$val[0]])) {
foreach ($parents[$val[0]] as $next) {
$sorted[] = $next;
}
}
}
Live demo: https://3v4l.org/Uk6Gs
I have two different version for you.
a) Using a "walk the tree" approach with recursion and references to minimize memory consumption
$data = [
[207306,'Bob',''], [199730,'Sam',199714],
[199728,'Simon',207306], [199714,'John',207306],
[199716, 'Tom',199718], [199718,'Phillip',207306],
[199720,'James',207306]
];
$list = [];
generateList($data, '', $list);
var_dump($list);
function generateList($data, $id, &$list) {
foreach($data as $d) {
if($d[2] == $id) {
$list[] = $d; // Child found, add it to list
generateList($data, $d[0], $list); // Now search for childs of this child
}
}
}
b) Using phps built in uusort()function (seems only to work up to php 5.x and not with php7+)
$data = [
[207306,'Bob',''], [199730,'Sam',199714],
[199728,'Simon',207306], [199714,'John',207306],
[199716, 'Tom',199718], [199718,'Phillip',207306],
[199720,'James',207306]
];
usort($data, 'cmp');
var_dump($data);
function cmp($a, $b) {
if($a[2] == '' || $a[0] == $b[2]) return -1; //$a is root element or $b is child of $a
if($b[2] == '' || $b[0] == $a[2]) return 1; //$b is root element or $a is child of $b
return 0; // both elements have no direct relation
}
I checked this works in PHP 5.6 and PHP 7
Sample array:
$array = Array(0 => Array(
0 => 207306,
1 => 'Bob',
2 => '',
),
1 => Array
(
0 => 199730,
1 => 'Sam',
2 => 199714,
),
2 => Array
(
0 => 199728,
1 => 'Simon',
2 => 207306,
),
3 => Array
(
0 => 199714,
1 => 'John',
2 => 207306,
),
4 => Array
(
0 => 199716,
1 => 'Tom',
2 => 199718,
),
5 => Array
(
0 => 199718,
1 => 'Phillip',
2 => 207306,
),
6 => Array
(
0 => 199720,
1 => 'James',
2 => 207306,
),
);
echo "<pre>";
$emp = array();
//form the array with parent and child
foreach ($array as $val) {
$manager = ($val[2] == '') ? 0 : $val[2];
$exist = array_search_key($val[2], $emp);
if ($exist)
$emp[$exist[0]][$val[0]] = $val;
else
//print_R(array_search_key(199714,$emp));
$emp[$manager][$val[0]] = $val;
}
$u_emp = $emp[0];
unset($emp[0]);
//associate the correct child/emp after the manager
foreach ($emp as $k => $val) {
$exist = array_search_key($k, $u_emp);
$pos = array_search($k, array_keys($u_emp));
$u_emp = array_slice($u_emp, 0, $pos+1, true) +
$val +
array_slice($u_emp, $pos-1, count($u_emp) - 1, true);
}
print_R($u_emp); //print the final result
// key search function from the array
function array_search_key($needle_key, $array, $parent = array())
{
foreach ($array AS $key => $value) {
$parent = array();
if ($key == $needle_key)
return $parent;
if (is_array($value)) {
array_push($parent, $key);
if (($result = array_search_key($needle_key, $value, $parent)) !== false)
return $parent;
}
}
return false;
}
Find the below code that might be helpful.So, your output is stored in $sortedarray.
$a=array(array(207306,'Bob',''),
array (199730,'Sam',199714),
array(199728,'Simon',207306),
array(199714,'John',207306),
array(199716,'Tom',199718),
array(199718,'Phillip',207306),
array(199720,'James',207306));
$sortedarray=$a;
foreach($a as $key=>$value){
$checkvalue=$value[2];
$checkkey=$key;
foreach($a as $key2=>$value2){
if($key<$key2){
if ($value2[0]===$checkvalue){
$sortedarray[$key]=$value2;
$sortedarray[$key2]=$value;
}else{
}
}
}
}
print_r($sortedarray);
What about this approach:
Create an empty array result.
Loop over your array and only take the items out of it where [2] is empty and insert them into result.
When this Loop is done you use a foreach-Loop inside a while-loop. With the foreach-Loop you take every item out of your array where [2] is already part of result. And you do this as long as your array contains anything.
$result = array();
$result[''] = 'root';
while(!empty($yourArray)){
foreach($yourArray as $i=>$value){
if(isset($result[$value[2]])){
// use the next line only to show old order
$value['oldIndex'] = $i;
$result[$value[0]] = $value;
unset($yourArray[$i]);
}
}
}
unset($result['']);
PS: You may run into trouble by removing parts of an array while walking over it. If you do so ... try to solve this :)
PPS: Think about a break condition if your array have an unsolved loop or a child without an parent.
you can use your array in variable $arr and use this code it will give you required output.
function check($a, $b) {
return ($a[0] == $b[2]) ? -1 : 1;
}
uasort($arr, 'check');
echo '<pre>';
print_r(array_values($arr));
echo '</pre>';

Foreach not working - PHP

I need to print the below array structure as:
Node Title 1
topic 1
topic 2
topic 3
topic 4
asset title1
asset title2
asset title3
How can i do using foreach - PHP
What i have done is :
foreach($output['fields'] as $key => $value) {
if($key == 'title') {
echo $value;
}
if(count($value['main_topic'])) {
foreach($value['main_topic'] AS $mainkey => $main_topic) {
echo $main_topic['topic_title'];
}
}
}
The above syntax is printing the title. But not the main_topic array.
Array
(
[fields] => Array
(
[nid] => 136
[node_title] => Node title 1
[node_type] => curriculum
[title] => Node title 1
[main_topic] => Array
(
[0] => Array
(
[row_id] => 136
[topic_id] => 411847
[weight] => 10
[topic_title] => topic 1
)
[1] => Array
(
[row_id] => 136
[topic_id] => 411839
[weight] => 2
[topic_title] => topic 2
)
[2] => Array
(
[row_id] => 136
[topic_id] => 411840
[weight] => 3
[topic_title] => topic 3
)
[3] => Array
(
[row_id] => 136
[topic_id] => 411841
[weight] => 4
[topic_title] => topic 4
[subfield] => Array
(
[1] => Array
(
[asset_title] => asset title 1
)
[2] => Array
(
[asset_title] => asset title 2
)
[3] => Array
(
[asset_title] => asset title 3
)
)
)
)
)
)
That is because you are iterating over all $output['fields'].
There will never be a $value with key 'main_topic' because the key 'main_topic' is contained in the $output['fields'] array and thus exists only as $key in your foreach. The array you want is $value
Your code should be like:
foreach($output['fields'] as $key => $value) {
if($key == 'title') {
echo $value;
continue;
}
if($key == 'main_topic' && is_array($value)) {
foreach($value as $main_topic) {
echo $main_topic['topic_title'];
}
}
}
To complete this answer with a full solution (including asset titles), below is how I would write it.
Because $output['fields'] is the starting point and to make the code more readable, I create a reference to the starting node using the =& operator so the array is not copied in memory. I do the same with the inner foreachs. Since we are not modifying data, referencing the variables is sufficient and consumes less memory and CPU:
if (is_array($output['fields'])) {
$node =& $output['fields'];
echo $node['title'];
if(is_array($node['main_topic'])) {
foreach($node['main_topic'] as &$main) {
echo $main['topic_title'];
if(is_array($main['subfield'])) {
foreach($main['subfield'] as &$asset) {
echo $asset['asset_title'];
}
}
}
}
}
else {
echo "no menu";
}
$value is the array, not $key['main_topic']
foreach($output['fields'] as $key => $value) {
if($key == 'title') {
echo $value;
}
if($key == 'main_topic') {
foreach($value as $mainkey => $main_topic) {
echo $main_topic['topic_title'];
}
}
}
Try this, you need the additional key:
echo $value['main_topic'][$mainkey]['topic_title'];
You're getting your array sections confused.
Try (and I haven't tested this) :
echo $output['node_title']."\n";
foreach ($output['fields'] as $key=>$value)
{
switch ($key)
{
case 'title':
echo $value."\n";
break;
case 'main_topic':
if (count($value) > 0)
{
foreach ($value as $main_block)
{
echo "\t".$main_block['topic_title']."\n";
if (array_key_exists('subfield',$main_block)!==FALSE)
{
foreach ($main_block['subfield'] as $subfield_block)
{
echo "\t\t".$subfield_block['asset_title']."\n";
}
}
}
}
break;
default:
break;
}
}

PHP: Iterating through array?

I want a function that
searches through my array, and
returns all the
children to a specific node. What is
the most appropriate way to do this?
Will recursion be necessary in this case?
I have previously constructed a few quite complex functions that iterates with or without the help of recursion through multi-dimensional arrays and re-arranging them, but this problem makes me completely stuck and I can't just get my head around it...
Here's my array:
Array
(
[1] => Array (
[id] => 1
[parent] => 0
)
[2] => Array (
[id] => 2
[parent] => 1
)
[3] => Array (
[id] => 3
[parent] => 2
)
)
UPDATE:
The output which I want to get. Sorry for the bad example, but I'll blame it on lack of knowledge on how to format the stuff I need to do :)
function getAllChildren($id) {
// Psuedocode
return $array;
}
getAllChildren(1); // Outputs the following:
Array
(
[2] => Array (
[id] => 2
[parent] => 1
)
[3] => Array (
[id] => 3
[parent] => 2
)
)
$nodes = array( 1 => array ( 'id' => 1,
'parent' => 0
),
2 => array ( 'id' => 2,
'parent' => 1
),
3 => array ( 'id' => 3,
'parent' => 2
)
);
function searchItem($needle,$haystack) {
$nodes = array();
foreach ($haystack as $key => $item) {
if ($item['parent'] == $needle) {
$nodes[$key] = $item;
$nodes = $nodes + searchItem($item['id'],$haystack);
}
}
return $nodes;
}
$result = searchItem('1',$nodes);
echo '<pre>';
var_dump($result);
echo '</pre>';
Non-recursive version of the searchItem() function:
function searchItem($needle,$haystack) {
$nodes = array();
foreach ($haystack as $key => $item) {
if (($item['parent'] == $needle) || array_key_exists($item['parent'],$nodes)) {
$nodes[$key] = $item;
}
}
return $nodes;
}
(assumes ordering of the parents/children, so a child node isn't included in the array unless the parent is already there)
<?php
function searchItem($needle)
{
foreach ($data as $key => $item)
{
if ($item['id'] == $needle)
{
return $key;
}
}
return null;
}
?>
Check out the array_walk_recursive() function in PHP:
http://www.php.net/manual/en/function.array-walk-recursive.php

Categories