PHP push a value to an array during recursive search - php

$menus = [
0 => [
'id' => 'home',
'title' => 'Home',
'url' => '/display/home',
'children' => [],
'parent' => null
],
1 => [
'id' => 'nodes',
'title' => 'Nodes',
'url' => 'nodes/index',
'children' => [
0 => [
'id' => 'addNode',
'title' => 'Add Node',
'url' => '/nodes/add',
'children' => [],
'parent' => "nodes"
],
1 => [
'id' => 'editNode',
'title' => 'Edit Node',
'url' => '/nodes/edit',
'children' => [],
'parent' => 'nodes'
],
2 => [
'id' => 'deleteNode',
'title' => 'Delete Node',
'url' => '/nodes/delete',
'children' => [
0 => [
'id' => 'deleteMultipleNodes',
'title' => 'Delete Multiple Nodes',
'url' => '/nodes/deleteall',
'children' => [
0 => [
'id' => 'deleteMultipleSelectedNodes',
'title' => 'Delete Multiple Selected Nodes',
'url' => '/nodes/deleteallselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
]
],
'parent' => 'deleteNode'
]
],
'parent' => 'nodes'
]
],
'parent' => null
]
];
Assuming I have this array. What i want is to recursively search this array for an "id" and if found push a new children to the children array of that element.
I've tried it via different ways, I've also tried to use RecursiveArrayIterator to traverse the array, but the problem is how can i push value to that index of the array when found while traversing.
For Example here is a code from one of my tries:
private function traverseArray($array)
{
$child = [
'id' => 'deleteMultipleNotSelectedNodes',
'title' => 'Delete Multiple Not Selected Nodes',
'url' => '/nodes/deletenotselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
];
foreach($array as $key=>$value)
{
if(is_array($value))
{
$this->traverseArray($value);
}
if($key == "id" && $value == "deleteMultipleNodes")
{
array_push($array["children"], $child); // This part is confusing me, How to add the child on this index where the id is found.
}
}
}
Any help on how to do such thing in an efficient way would save my days.

Here it's how it would work without using $this and fixing bugs in comparing $value instead assigning anything to value.
Please note the difference with &$array and &$value, which are references, so it would change the original data instead of copying it into new variables.
<?php
$menus = [
0 => [
'id' => 'home',
'title' => 'Home',
'url' => '/display/home',
'children' => [],
'parent' => null
],
1 => [
'id' => 'nodes',
'title' => 'Nodes',
'url' => 'nodes/index',
'children' => [
0 => [
'id' => 'addNode',
'title' => 'Add Node',
'url' => '/nodes/add',
'children' => [],
'parent' => "nodes"
],
1 => [
'id' => 'editNode',
'title' => 'Edit Node',
'url' => '/nodes/edit',
'children' => [],
'parent' => 'nodes'
],
2 => [
'id' => 'deleteNode',
'title' => 'Delete Node',
'url' => '/nodes/delete',
'children' => [
0 => [
'id' => 'deleteMultipleNodes',
'title' => 'Delete Multiple Nodes',
'url' => '/nodes/deleteall',
'children' => [
0 => [
'id' => 'deleteMultipleSelectedNodes',
'title' => 'Delete Multiple Selected Nodes',
'url' => '/nodes/deleteallselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
]
],
'parent' => 'deleteNode'
]
],
'parent' => 'nodes'
]
],
'parent' => null
]
];
function traverseArray(&$array)
{
$child = [
'id' => 'deleteMultipleNotSelectedNodes',
'title' => 'Delete Multiple Not Selected Nodes',
'url' => '/nodes/deletenotselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
];
foreach($array as $key=>&$value)
{
if(is_array($value))
{
traverseArray($value);
}
if($key == "id" && $value == "deleteMultipleNodes")
{
array_push($array["children"], $child);
}
}
}
echo "=== before \n";
var_export($menus);
echo "\n\n";
traverseArray($menus);
echo "=== after \n";
var_export($menus);

Related

How I can flatten a tree in an orderly way?

I have PHP array (tree), something like this:
$categoryTree = [
0 => [
'id' => 1360,
'parent' => 0,
'name' => 'main A',
'children' => [
0 => [
'id' => 1361,
'parent' => 1360,
'name' => 'sub a1'
],
1 => [
'id' => 57,
'parent' => 1360,
'name' => 'sub a2'
]
]
],
1 => [
'id' => 10,
'parent' => 0,
'name' => 'Main B'
]
];
I want to convert it into:
$categoryTree = [
0 => [
'id' => 1360,
'parent' => 0,
'name' => 'main A'
],
1 => [
'id' => 1361,
'parent' => 1360,
'name' => 'sub a1'
],
2 => [
'id' => 57,
'parent' => 1360,
'name' => 'sub a2'
],
3 => [
'id' => 10,
'parent' => 0,
'name' => 'Main B'
]
];
It is rather simple. You walk recursively and only make a recursive call if the node has the key children. During the iteration in the foreach loop, keep collecting results in an result array.
<?php
function flatten($tree, &$results){
foreach($tree as $kid){
$kid_copy = $kid;
unset($kid_copy['children']);
$results[] = $kid_copy;
if(isset($kid['children'])) flatten($kid['children'], $results);
}
}
Online Demo
Alternatively, using a generator, which then doesn't need the second argument:
function recurIter($arr) {
foreach ($arr as $item) {
$orig = $item;
unset($item["children"]);
yield $item;
if (isset($orig["children"])) yield from recurIter($orig["children"]);
}
}
$result = iterator_to_array(recurIter($categoryTree), false);

Restructure multidimensional array based on associative array key

I have the following array:
$products = [
[
'id' => 21,
'name' => 'Floral Dress',
'params' => [
'size' => 14,
'price' => 23.99,
'department' => 'Dresses'
]
],
[
'id' => 413,
'name' => 'Maxi Skirt',
'params' => [
'size' => 10,
'price' => 12.99,
'department' => 'Skirts'
]
],
[
'id' => 78,
'name' => 'A Line Prom Dress',
'params' => [
'size' => 10,
'price' => 79.99,
'department' => 'Dresses'
]
],
[
'id' => 212,
'name' => 'Nude Block High Heels',
'params' => [
'size' => 6,
'price' => 20.99,
'department' => 'Shoes'
]
],
[
'id' => 54,
'name' => 'Denim Trim Dress',
'params' => [
'size' => 8,
'price' => 52.99,
'department' => 'Dresses'
]
],
];
Unfortunately I cannot control how the array is built.
Instead I need to restructure the data so that the array is sorted by the ['params']['department'] key.
So ideally I would take the above and output it like so:
$products = [
'Dresses' => [
[
'id' => 21,
'name' => 'Floral Dress',
'params' => [
'size' => 14,
'price' => 23.99
]
],
[
'id' => 78,
'name' => 'A Line Prom Dress',
'params' => [
'size' => 10,
'price' => 79.99
]
],
[
'id' => 54,
'name' => 'Denim Trim Dress',
'params' => [
'size' => 8,
'price' => 52.99
]
],
],
'Skirts' => [
[
'id' => 413,
'name' => 'Maxi Skirt',
'params' => [
'size' => 10,
'price' => 12.99
]
],
],
'Shoes' => [
[
'id' => 212,
'name' => 'Nude Block High Heels',
'params' => [
'size' => 6,
'price' => 20.99
]
],
]
You could simply iterate over all the products and populate a new associative array holding the departments, which are themselves arrays of corresponding products. See the comments in the code below.
<?php
// same data as in the question, just in one line for better overview
$products = [['id' => 21,'name' => 'Floral Dress','params' => ['size' => 14,'price' => 23.99,'department' => 'Dresses']],['id' => 413,'name' => 'Maxi Skirt','params' => ['size' => 10,'price' => 12.99,'department' => 'Skirts']],['id' => 78,'name' => 'A Line Prom Dress','params' => ['size' => 10,'price' => 79.99,'department' => 'Dresses']],['id' => 212,'name' => 'Nude Block High Heels','params' => ['size' => 6,'price' => 20.99,'department' => 'Shoes']],['id' => 54,'name' => 'Denim Trim Dress','params' => ['size' => 8,'price' => 52.99,'department' => 'Dresses']]];
// initialize empty department array
$departments = array();
// iterate over all the products
foreach ( $products as $product ) {
// get this product's department
$product_department = $product['params']['department'];
// unset the department in the params array
unset( $product['params']['department'] );
// initialize this department as an empty array
// if it doesn't exist yet
if ( !isset( $departments[$product_department] ) ) {
$departments[$product_department] = array();
}
// push this product in its corresponding department
$departments[$product_department][] = $product;
}
var_dump( $departments );
$groupedProducts = [];
foreach ($products as &$product) {
$department = $product['params']['department'];
unset($product['params']['department']);
$groupedProducts[$department] = $product;
}
var_dump($groupedProducts);

Invalid argument supplied for foreach() when i try to print value

This is my array
[
'field_test1' => [
'field_test2' => [ 'value' => 'Yes' ,'action' => 'visible']
],
'field_test3' => [
'field_test4' => [ 'value' => '2' ,'action' => 'visible']
]
'body' => [
'field_test2' => [ 'value' => 'No', 'action' => 'visible']
'field_test4' => [ 'value' => '1', 'action' => 'visible']
]
]
When i try to loop through each element i am getting error like invalid argument passed for foreach;
My code is
foreach ($myArray as $key => $value) {
echo $key;
}
what should i do??
You forgot the comma after your second array within your array. Before the one with the 'body' key. Try using a decent IDE like PhpStorm, it will highlight the errors in your syntax for you, making the search for common errors easy.
$myArray =
[
'field_test1' => [
'field_test2' => [ 'value' => 'Yes' ,'action' => 'visible']
],
'field_test3' => [
'field_test4' => [ 'value' => '2' ,'action' => 'visible']
],
'body' => [
'field_test2' => [ 'value' => 'No', 'action' => 'visible']
'field_test4' => [ 'value' => '1', 'action' => 'visible']
],
];

PHP Recursive Iteration for Nested Array

I have following array, the depth of array cannot be known since array can have n childs.
$menu = [
[
'name' => 'home',
'label' => 'Home',
'uri' => '/home',
'order' => 1,
'attributes' => [
'class' => ['home-class', 'home-class-2'],
'id' => ['home-id']
]
], [
'name' => 'about',
'label' => 'About',
'uri' => '/about',
'order' => 2,
'attributes' => [
'class' => [],
'id' => []
],
'child' => [
[
'name' => 'company_profile',
'label' => 'Company Profile',
'uri' => '/company-profile',
'order' => 1,
'attributes' => [
'class' => [],
'id' => []
]
], [
'name' => 'team',
'label' => 'Team',
'uri' => '/team',
'order' => 2,
'attributes' => [
'class' => ['team-class', 'team-class-2'],
'id' => ['team-id']
],
'child' => [
[
'name' => 'management_team',
'label' => 'Management Team',
'uri' => '/management-team',
'order' => 1,
'attributes' => [
'class' => [],
'id' => []
]
],
[
'name' => 'development_team',
'label' => 'Development Team',
'uri' => '/development-team',
'order' => 2,
'attributes' => [
'class' => [],
'id' => []
]
],
]
],
]
], [
'name' => 'services',
'label' => 'Services',
'uri' => '/services',
'order' => 3,
'attributes' => [
'class' => [],
'id' => []
],
'child' => [
[
'name' => 'web_application',
'label' => 'Web Application',
'uri' => '/web-application',
'order' => 1,
'attributes' => [
'class' => [],
'id' => []
]
], [
'name' => 'mobile_application',
'label' => 'Mobile Application',
'uri' => '/mobile-application',
'order' => 2,
'attributes' => [
'class' => [],
'id' => []
]
], [
'name' => 'cms_development',
'label' => 'CMS Development',
'uri' => '/cms-development',
'order' => 3,
'attributes' => [
'class' => [],
'id' => []
]
],
]
]
];
I want to loop this over and pass data to object, for example.
$nav = new Navigation\Menu('main');
foreach ($menu as $item) {
// Parent element
$navItem = new Navigation\Item($item['name']);
$navItem->setLabel($item['label']);
$navItem->setUri($item['uri']);
$nav->addItem($navItem);
if (isset($item['child']) && is_array($item['child'])) {
// First child
foreach ($item['child'] as $child1) {
$childItem1 = new Navigation\Item($child1['name']);
$childItem1->setLabel($child1['label']);
$childItem1->setUri($child1['uri']);
$navItem->addChild($childItem1);
if (isset($child1['child']) && is_array($child1['child'])) {
// Second child
foreach ($child1['child'] as $child2) {
$childItem2 = new Navigation\Item($child2['name']);
$childItem2->setLabel($child2['label']);
$childItem2->setUri($child2['uri']);
$childItem1->addChild($childItem2);
}
}
}
}
}
This works but with a problem. As you see, I am manually looping over each child, I do not want this, what i am looking for is, It must iterate the array recursively allowing to add any number of child with any depth.
I tried array_walk_recursive or custom recursive function without any result. any pointer to solve this is appreciated.
Thanks.
Here is a little recursive script that should run through every if it is an array or an object, each recursion will return the object. Now this would need some editing for your usage. But it should give you a starting point.
function Navigation($item) {
if (is_object($item)) {
foreach (get_object_vars($item) as $property => $value) {
//If item is an object, then run recursively
if (is_array($value) || is_object($value)) {
$item->$property = Navigation($item);
} else {
$navItem->setLabel($item['label']);
$navItem->setUri($item['uri']);
$nav->addItem($navItem);
}
}
return $nav;
} elseif (is_array($item)) {
foreach ($item as $property => $value) {
//If item is an array, then run recursively
if (is_array($value) || is_object($value)) {
$item[$property] = Navigation($item);
} else {
$navItem->setLabel($item['label']);
$navItem->setUri($item['uri']);
$nav->addItem($navItem);
}
}
return $nav;
}
$navItem->setLabel($item['label']);
$navItem->setUri($item['uri']);
$nav->addItem($navItem);
}
Figured it out finally.
Here is how i did it using custom recursive function.
function recursive($menu, &$nav, $child = false, $parent = null)
{
foreach ($menu as $page) {
$navItem = new Navigation\Item($page['name']);
if (false == $child) {
$nav->addItem($navItem);
} else {
$parent->addChild($navItem);
}
if (isset($page['child'])) {
recursive($page['child'], $nav, true, $navItem);
}
}
}
$nav = new Navigation\Menu('main');
recursive($menu, $nav);

Nesting an array based on parent IDs

I've been stuck trying to figure this out for a week now. I have an array in the following format:
[
1 => [
'name' => 'Maths',
'parent_category_id' => NULL
],
2 => [
'name' => 'Algebra',
'parent_category_id' => 1
],
3 => [
'name' => 'Expanding brackets',
'parent_category_id' => 2
],
4 => [
'name' => 'Factorising brackets',
'parent_category_id' => 2
],
5 => [
'name' => 'English',
'parent_category_id' => NULL
],
6 => [
'name' => 'Shakespeare',
'parent_category_id' => 5
]
]
and I want to transform it into an array in this format:
[
'Maths' => [
'category_id' => 1,
'questions' => [], //This array will then be filled with questions regarding each of the categories
'children_categories' => [
'Algebra' => [
'category_id' => 2,
'questions' => [],
'children_categories' => [
'Expanding brackets' => [
'category_id' => 3,
'questions' => [],
'children_categories' => []
],
'Factorising brackets' => [
'category_id' => 4,
'questions' => [],
'children_categories' => []
]
]
]
]
],
'English' => [
'category_id' => 5,
'questions' => [],
'children_categories' => [
'Shakespeare' => [
'category_id' => 6,
'questions' => [],
'children_categories' => []
]
]
]
]
So far I've been able to format categories that have no parents, but I for the life of me cannot figure out how to insert a category that has a parent into that parent's children_categories[] array. Here's the code I'm using, I need help figuring out what I should put in the "else" part of the foreach()
foreach($ids_as_keys as $category_id => $info){ //$info contains the name of the category, and the parent's ID (NULL if there is no parent)
if(is_null($info['parent_category_id'])){ //There is no parent, so put it at the root of $nested
$nested[$info['name']] = [
'category_id' => $category_id,
'questions' => [],
'children_categories' => []
];
}else{ //There is a parent, so search through all items (including sub-arrays, sub-sub-arrays etc.) until we find a match for the parent_category_id, and then add it into the children_categories[] array
}
}
return $nested;
I tested this, absolutely works :
$arr = array(
1 => array(
'name' => 'Maths',
'parent_category_id' => NULL
),
2 => array(
'name' => 'Algebra',
'parent_category_id' => 1
),
3 => array(
'name' => 'Expanding brackets',
'parent_category_id' => 2
),
4 => array(
'name' => 'Factorising brackets',
'parent_category_id' => 2
),
5 => array(
'name' => 'English',
'parent_category_id' => NULL
),
6 => array(
'name' => 'Shakespeare',
'parent_category_id' => 5
)
);
foreach ($arr as $key => &$value) {
if ($value['parent_category_id']) {
$arr[$value['parent_category_id']]['children_categories'][] = &$value;
}
else{
$parents[]=$key;
}
}
$result = array();
foreach ($parents as $val) {
$result[$val] = $arr[$val];
}
print_r($result);
This answer is too close may helpful for you
<?php
$array = [
1 => [
'name' => 'Maths',
'parent_category_id' => NULL
],
2 => [
'name' => 'Algebra',
'parent_category_id' => 1
],
3 => [
'name' => 'Expanding brackets',
'parent_category_id' => 2
],
4 => [
'name' => 'Factorising brackets',
'parent_category_id' => 2
],
5 => [
'name' => 'English',
'parent_category_id' => NULL
],
6 => [
'name' => 'Shakespeare',
'parent_category_id' => 5
]
];
//data array
$data = array();
$i = 0;
//gothrough one by one
foreach($array as $key=>$value)
{
//set the parent array
if(is_null($value['parent_category_id']))
{
$data[$value['name']]= array();
$data[$value['name']]['category_id'] = $key;
$data[$value['name']]['questions'] = array();
$data[$value['name']]['children_categories'] = array();
//add the childrens according to the parent
}elseif(array_key_exists($value['parent_category_id'], $array)){
//find the parent
$parent = $array[$value['parent_category_id']];
$data[$parent['name']]['children_categories'][$value['name']] = array();
$data[$parent['name']]['children_categories'][$value['name']]['category_id'] = $key;
$data[$parent['name']]['children_categories'][$value['name']]['questions'] = array();
}
}
//display the result
print_r($data);
Try using recursive function.
function insert_child($curArr,$childArray,&$parentArray){
foreach($parentArray as $key=>&$val){
if(is_array($val) && sizeof($val) > 0 ){
if($val['category_id']==$curArr['parent_category_id']){
$val['children_categories'][$curArr['name']] = $childArray;
return TRUE;
}else{
insert_child($curArr,$childArray,$val['children_categories']);
}
}
}
return FALSE;
}
Where $nest is your input array
$nest = array(
1=>array(
'name' => 'Maths',
'parent_category_id' => NULL
),
2=>array(
'name' => 'Algebra',
'parent_category_id' => 1
),
3=>array(
'name' => 'Expanding brackets',
'parent_category_id' => 2
),
4=>array(
'name' => 'Factorising brackets',
'parent_category_id' => 2
),
5=>array(
'name' => 'English',
'parent_category_id' => NULL
),
6=>array(
'name' => 'Shakespeare',
'parent_category_id' => 5
),
);
code
$result=array();
foreach($nest as $key=>$val){
$temp = array(
'category_id'=>$key,
'questions'=>array(),
'children_categories'=>array(),
);
if(!in_array($val['name'],$result) && $val['parent_category_id']==NULL){
$result[$val['name']] = $temp;
}else{
insert_child($val,$temp,$result);
}
}
echo "<pre>";
print_r($result);
exit();
PHPFIddle here

Categories