Return result of recursive function - php

I got a recursive function which currently echo the results. I want this function to return results and loop them with foreach inside markup.
I understand that i should use some kind of iteration for each array to get my desired result but have failed with that. Currently my code look like this(with attempts to iterate):
public static function recursiveChildCategory($categories = array(), $depth = 0, $i = 0) {
$ca = [];
// Loop through categories
foreach($categories as $key => $category){
echo str_repeat(" ", $depth);
echo "<a href='".implode('/', $category['breadcrumb'])."'>{$category['title']}</a>";
echo '<br>';
$ca[$i] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
self::recursiveChildCategory($category['child'], $depth + 1, $i++);
}
}
return $ca;
}
And incoming array to the function:
Array (
[0] => Array (
[id] => 7
[title] => Deserts
[slug] => deserts
[child] => Array (
[0] => Array (
[id] => 8
[title] => Space
[slug] => space
[child] => Array (
[0] => Array (
[id] =>
[title] =>
[slug] =>
[child] => Array ( )
)
)
)
)
)
)
Currently it just returns first level of child categories "Deserts", but nothing about "Space".
As desired result i want function to return all categories with $depth and infinite path to multiple child categoires (to do the same work as currently echo doing).
Thanks in advice

Try this and tell me if it's what you are looking for :
The array for my test :
$array = [
0 => [
"id" => 7,
"title" => "Deserts",
"slug" => "deserts",
"child" => [
0 => [
"id" => 8,
"title" => "Space",
"slug" => "space",
"child" => [
0 => [
"id" => 9,
"title" => "Test",
"slug" => "test"
]
]
]
]
]
];
The recursive function :
function recursiveChildCategory($categories, $depth = 0, $ca = []) {
// Loop through categories
foreach($categories as $key => $category){
$ca[$depth] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
$ca = recursiveChildCategory($category['child'], $depth + 1, $ca);
} else {
break;
}
}
return $ca;
}
Now, how to use it :
$test = recursiveChildCategory($array);
var_dump($test);
And this is the output :
array(3) {
[0]=>
array(2) {
["id"]=>
int(7)
["title"]=>
string(7) "Deserts"
}
[1]=>
array(2) {
["id"]=>
int(8)
["title"]=>
string(5) "Space"
}
[2]=>
array(2) {
["id"]=>
int(9)
["title"]=>
string(4) "Test"
}
}
Here is a link to test it : http://sandbox.onlinephpfunctions.com/
EDIT : I made some modification because in OP example array can have multiple result in first "depth", here is the "new" solution :
The array for test :
$array = [
"0" =>
[ "id" => 3, "title" => "Subcat", "slug" => "subcat", "child" =>
[ "0" =>
[ "id" => 5, "title" => "Subsubcat2", "slug" => "subcat2", "child" =>
[ "0" =>
[ "id" => "", "title" => "", "slug" =>"", "breadcrumb" => [ "0" => "homeworld", "1" => "cat", "2" => "subcat", "3" => "subcat2" ], "child" => [ ] ]
]
]
]
],
"1" =>
[ "id" => 4, "title" => "Kalahari", "slug" => "kalahari", "child" =>
[ "0" => [ "id" => 7, "title" => "deserts", "slug" => "deserts", "child" =>
[ "0" =>
[ "id" => 8, "title" => "Ural", "slug" => "ural", "child" =>
[ "0" => [ "id" =>"", "title" =>"", "slug" =>"", "child" => [ ] ] ]
]
]
]
]
]
];
The function : I just add $ca[$depth][] instead of $ca[$depth]
function recursiveChildCategory($categories, $depth = 0, $ca = []) {
// Loop through categories
foreach($categories as $key => $category){
$ca[$depth][] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
$ca = recursiveChildCategory($category['child'], $depth + 1, $ca);
} else {
break;
}
}
return $ca;
}
And now the result :
$test = recursiveChildCategory($array);
foreach ($test as $depth => $c) {
echo "depth : ".$depth."\n";
foreach ($c as $result) {
echo "Title : ".$result['title']."\n";
}
echo "=============\n";
}
The output is :
depth : 0
Title : Subcat
Title : Kalahari
=============
depth : 1
Title : Subsubcat2
Title : deserts
=============
depth : 2
Title :
Title : Ural
=============
depth : 3
Title :
=============
Test here : link

Related

PHP - Flat Associative Array into Deeply nested Array by parent property

I have a problem that has been stressing me out for weeks now and i cannot find a clean solution to it that does not involve recursion.
This is the problem:
Take a flat array of nested associative arrays and group this into one deeply nested object. The top level of this object will have its parent property as null.
This is my solution but i admit it is far from perfect. I am fairly certain this can be done in a single loop without any recursion, but for the life of me i cannot work it out!
//Example single fork
$data = array(
//Top of Tree
0 => array(
"name" => "A",
"parent" => null,
"id" => 1,
),
//B Branch
1 => array(
"name" => "B",
"parent" => "1",
"id" => 2,
),
2 => array(
"name" => "B1",
"parent" => "2",
"id" => 3,
),
3 => array(
"name" => "B2",
"parent" => "3",
"id" => 4,
),
4 => array(
"name" => "B3",
"parent" => "4",
"id" => 5,
),
//C Branch
5 => array(
"name" => "C",
"parent" => "1",
"id" => 6,
),
6 => array(
"name" => "C1",
"parent" => "6",
"id" => 7,
),
7 => array(
"name" => "C2",
"parent" => "7",
"id" => 8,
),
8 => array(
"name" => "C3",
"parent" => "8",
"id" => 9,
),
);
Actual anonymised example
array:7214 [▼
0 => array:3 [▼
"name" => ""
"parent" => null
"id" =>
]
1 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
2 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
3 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
4 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
5 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
6 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
7 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
8 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
9 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
10 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
Another example deeper nesting
{
"name":"top",
"id":xxx,
"children":{
"second":{
"name":"second",
"id":xxx,
"children":{
"Third":{
"name":"third",
"id":xxx,
"children":{
"fourth":{
"name":"fourth",
"id":xxx
}
}
}
}
}
}
}
$originalLength = count($data);
$obj = [];
while ($originalLength > 0) {
foreach ($data as $item) {
$name = $item['name'];
$parent = $item['parent'];
$a = isset($obj[$name]) ? $obj[$name] : array('name' => $name, 'id'=>$item['id']);
if (($parent)) {
$path = get_nested_path($parent, $obj, array(['']));
try {
insertItem($obj, $path, $a);
} catch (Exception $e) {
continue;
//echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
$obj[$name] = isset($obj[$name]) ? $obj[$name] : $a;
$originalLength--;
}
}
echo json_encode($obj['A']);
function get_nested_path($parent, $array, $id_path)
{
if (is_array($array) && count($array) > 0) {
foreach ($array as $key => $value) {
$temp_path = $id_path;
array_push($temp_path, $key);
if ($key == "id" && $value == $parent) {
array_shift($temp_path);
array_pop($temp_path);
return $temp_path;
}
if (is_array($value) && count($value) > 0) {
$res_path = get_nested_path(
$parent, $value, $temp_path);
if ($res_path != null) {
return $res_path;
}
}
}
}
return null;
}
function insertItem(&$array, $path, $toInsert)
{
$target = &$array;
foreach ($path as $key) {
if (array_key_exists($key, $target))
$target = &$target[$key];
else throw new Exception('Undefined path: ["' . implode('","', $path) . '"]');
}
$target['children'] = isset($target['children']) ? $target['children'] : [];
$target['children'][$toInsert['name']] = $toInsert;
return $target;
}
Here's my take on what I believe is the desired output:
function buildTree(array $items): ?array {
// Get a mapping of each item by ID, and pre-prepare the "children" property.
$idMap = [];
foreach ($items as $item) {
$idMap[$item['id']] = $item;
$idMap[$item['id']]['children'] = [];
}
// Store a reference to the treetop if we come across it.
$treeTop = null;
// Map items to their parents' children array.
foreach ($idMap as $id => $item) {
if ($item['parent'] && isset($idMap[intval($item['parent'])])) {
$parent = &$idMap[intval($item['parent'])];
$parent['children'][] = &$idMap[$id];
} else if ($item['parent'] === null) {
$treeTop = &$idMap[$id];
}
}
return $treeTop;
}
This does two array cycles, one to map up the data by ID, then one to assign children to parents. Some key elements to note:
The build of $idMap in the first loop also effectively copies the items here so we won't be affecting the original input array (Unless it already contained references).
Within the second loop, there's usage of & to use references to other items, otherwise by default PHP would effectively create a copy upon assignment since these are arrays (And PHP copies arrays on assignment unlike Objects in PHP or arrays in some other languages such as JavaScript). This allows us to effectively share the same array "item" across the structure.
This does not protect against bad input. It's possible that invalid mapping or circular references within the input data could cause problems, although our function should always just be performing two loops, so should at least not get caught in an infinite/exhaustive loop.

Refactor foreach with multilevel array

I'm currently returning results from a sql statement in an array like so:
$results = [];
foreach($promotionTool as $p){
$results[] = $p;
}
return $results;
Which my console shows in an object with this structure:
(2) [{…}, {…}]
0:
codeID: "41"
code: "123ABC"
rule_type: "Category"
attribute_type: "identifier"
attribute_title: "category number"
attribute_value: "234"
1:
codeID: "41"
code: "123ABC"
rule_type: "Category"
attribute_type: "amount"
attribute_title: "percent"
attribute_value: "25"
This is showing the data I expect but I'm a little bit lost on how to restructure this so that I can group on certain levels and finally return only an array of the attributes like so:
codeID
code
rule_type
array(
0:
attribute_type: "identifier"
attribute_title: "category number"
attribute_value: "234"
1:
attribute_type: "amount"
attribute_title: "percent"
attribute_value: "25"
)
How would I refactor my foreach to group at multiple levels in that way?
I guess this is what you are looking for:
<?php
$input = [
[
'codeID' => "41",
'code' => "123ABC",
'rule_type' => "Category",
'attribute_type' => "identifier",
'attribute_title' => "category number",
'attribute_value' => "234"
],
[
'codeID' => "41",
'code' => "123ABC",
'rule_type' => "Category",
'attribute_type' => "amount",
'attribute_title' => "percent",
'attribute_value' => "25"
]
];
$output = [];
array_walk($input, function ($e) use (&$output) {
$output[$e['codeID']][$e['code']][$e['rule_type']][] = [
'attribute_type' => $e['attribute_type'],
'attribute_title' => $e['attribute_title'],
'attribute_value' => $e['attribute_value']
];
});
print_r($output);
This could be a variant easier to read:
array_walk($input, function ($e) use (&$output) {
$codeID = &$e['codeID'];
$code = &$e['code'];
$rule_type = &$e['rule_type'];
$output[$codeID][$code][$rule_type][] = [
'attribute_type' => $e['attribute_type'],
'attribute_title' => $e['attribute_title'],
'attribute_value' => $e['attribute_value']
];
});
The output obviously is:
Array
(
[41] => Array
(
[123ABC] => Array
(
[Category] => Array
(
[0] => Array
(
[attribute_type] => identifier
[attribute_title] => category number
[attribute_value] => 234
)
[1] => Array
(
[attribute_type] => amount
[attribute_title] => percent
[attribute_value] => 25
)
)
)
)
)

How to push the condition based value in existing array in PHP

I am having three arrays
topicsSelected
relavantGroups
topicAssingned
$topicsSelected = [ "T-100","T-600"];
$relavantGroups = [
[ "id" => "G-001","name" => "3 A","active" => false ],
["id" => "G-002","name" => "3 B","active" => false]
];
$topicAssingned = [
"G-001" => [
"groupID" => "G-001",
"groupName" => "3 A",
"topics" => [
"T-100" => [
"topicID" => "T-100"
],
"T-200" => [
"topicID" => "T-200"
]
]
],
"G-002" => [
"groupID" => "G-002",
"groupName" => "3 B",
"topics" => [
"T-400" => [
"topicID" => "T-400"
],
"T-500" => [
"topicID" => "T-500"
]
]
],
];
$topicsSelected array values at least one value should present $topicAssingned means based on groupID, i have to push one value to $relavantGroups like disable : D suppose value not present means disable : A
Expected output:
[
"id" => "G-001",
"name" => "3 A",
"active" => false,
"disable" => "D"
],
[
"id" => "G-002",
"name" => "3 B",
"active" => false,
"disable" => "A"
]
<?php
$topicsSelected = [ "T-100","T-600"];
$relavantGroups = [
[ "id" => "G-001","name" => "3 A","active" => false ],
["id" => "G-002","name" => "3 B","active" => false]
];
$topicAssigned = [
"G-001" => [
"groupID" => "G-001",
"groupName" => "3 A",
"topics" => [
"T-100" => [
"topicID" => "T-100"
],
"T-200" => [
"topicID" => "T-200"
]
]
],
"G-002" => [
"groupID" => "G-002",
"groupName" => "3 B",
"topics" => [
"T-400" => [
"topicID" => "T-400"
],
"T-500" => [
"topicID" => "T-500"
]
]
],
];
$topic_selected_map = [];
foreach($topicsSelected as $each_topic){
$topic_selected_map[$each_topic] = true;
}
$relevant_group_map = [];
foreach($relavantGroups as $each_group){
$relevant_group_map[$each_group['id']] = $each_group;
}
$result = [];
foreach($topicAssigned as $each_assigned_topic){
if(!isset($relevant_group_map[$each_assigned_topic['groupID']])) continue;
$topics_not_found = true;
foreach($each_assigned_topic['topics'] as $each_topic => $topic_details){
if(isset($topic_selected_map[$each_topic])){
$topics_not_found = false;
break;
}
}
$result[] = [
'id' => $each_assigned_topic['groupID'],
'name' => $each_assigned_topic['groupName'],
'active' => $relevant_group_map[$each_assigned_topic['groupID']]['active'],
'disable' => ($topics_not_found === true ? 'A' : 'D')
];
}
print_r($result);
Output:
Array
(
[0] => Array
(
[id] => G-001
[name] => 3 A
[active] => false
[disable] => D
)
[1] => Array
(
[id] => G-002
[name] => 3 B
[active] => false
[disable] => A
)
)
First, make a map(associative array) of values of $topicsSelected. Same goes for $relavantGroups. This is to make the check more efficient. See more on Hash Table.
Now, iterate over $topicAssigned and then iterate over each group's topics inside it. Now, check if a topic exists inside $topicsSelected using a simple isset function. If yes, we disable them, else we don't.
It's not very clear what you are asking and the code is a bit weird but I'll give it a try.
First fix your array declaration - you should use => and not :;
You have to Iterate over the $relavantGroups and for each element iterate the $topicAssingned array. Then perform a simple comparison to see if the group Id is present and you are done!
Here is my solution (quick and dirty): You can see it here
foreach ($relavantGroups as &$g) {
$found = false;
foreach ($topicAssingned as $key => $assigned) {
if ($key === $g["id"] && is_array($assigned["topics"])) {
foreach ($assigned["topics"] as $topic) {
if (in_array($topic["topicID"], $topicsSelected)) {
$found = true;
break;
}
}
}
}
$g["disable"] = $found ? "D" : "A";
}
var_dump($relavantGroups);
Updated the solution - note that I'm using in_array() to determine if the topicID is present. That mean that any value that is in the $topicsSelected array will affect the result.
Hope I helped.
This will output (based one your example):
array(2) {
[0]=> array(4) {
["id"]=> string(5) "G-001"
["name"]=> string(3) "3 A"
["active"]=> bool(false)
["disable"]=> string(1) "D"
}
[1]=> array(4) {
["id"]=> string(5) "G-002"
["name"]=> string(3) "3 B"
["active"]=> bool(false)
["disable"]=> string(1) "A"
}
}

PHP Combine keys from different arrays

I have an array that looks like this
"name" => array:3 [
1 => "Hello"
4 => "Test"
21 => "Test2"
]
"runkm" => array:3 [
1 => "100.00"
4 => "1000.00"
21 => "2000.00"
]
"active" => array:3 [
1 => "1"
4 => "0"
21 => "0"
]
Can i somehow combine the matching keys with a PHP function so that the array would look like this instead
1 => array:3 [
name => "Hello"
runkm => "100.00"
active => "1"
]
4 => array:3 [
name => "Test"
runkm => "1000.00"
active => "0"
]
21 => array:3 [
name => "Test2"
runkm => "2000.00"
active => "0"
]
EDIT: Thanks for all the answers guys. What i was really looking for was a PHP built in function for this, which i probably should have been more clear about.
$newArr=array();
foreach($array1 as $key => $value){
$newArr[$key]=>array(
'name' =>$value[$key];
'runkm' =>$array2[$key];
'active'=>$array3[$key];
);
}
this is how you make a new array and then print the $newArr and check you get what you want or not? Good Luck!
<?php
$resultarr = array();
for($i=0;$i<count($sourcearr['name']);$i++) {
$resultarr[] = array('name'=>$sourcearr['name'][$i], 'runkm'=>$sourcearr['runkm'][$i], 'active'=>$sourcearr['active'][$i]);
}
This works well. And also, doesn't use hard coded keys.
<?php
$arr = [
"name" => [
1 => "Hello",
4 => "Test",
21 => "Test2"
],
"runkm" => [
1 => "100.00",
4 => "1000.00",
21 => "2000.00"
],
"active" => [
1 => "1",
4 => "0",
21 => "0"
]
];
// Pass the array to this function
function extractData($arr){
$newarr = array();
foreach ($arr as $key => $value) {
foreach($value as $k => $v) {
if(!isset($newarr[$k]))
$newarr[$k] = array();
$newarr[$k][$key] = $v;
}
}
return $newarr;
}
print_r(extractData($arr));
?>
I'm not sure if there's a function that does that in PHP but maybe you can try this
$arr1 = array(
"name" => array(
1 => "hello",
4 => "test",
21 => "test2",
),
"runKm" => array(
1 => "100",
4 => "200",
21 => "300",
),
"active" => array(
1 => "1",
4 => "0",
21 => "0",
),
);
// declare another that will hold the new structure of the array
$nArr = array();
foreach($arr1 as $key => $val) {
foreach($val as $sub_key => $sub_val) {
$nArr[$sub_key][$key] = $sub_val;
}
}
what this does is simply loop thru each array and its values and assign it to another array which is the $nArr. I hope it helps.

Multidimensional array concatenate values of the same id(key)

How can I concatenate a key value if the id(key) value is the same as other id(key) value
PHP
$locations = Array(
[0] => Array(
"id" => 1,
"latitude" => "51.541561",
"longitude", => "84.215",
"content", => "The quick brown"
)
[1] => Array(
"id" => 1,
"latitude" => "51.541561",
"longitude", => "84.215",
"content", => "fox jumps over the lazy dog"
)
[2] => Array(
"id" => 3,
"latitude" => "12.541561",
"longitude", => "32.215",
"content", => "Another content"
)
)
And I want to make it like this:
$locations = Array(
[0] => Array(
"id" => 1,
"latitude" => "51.541561",
"longitude", => "84.215",
"content", => "The quick brown fox jumps over the lazy dog"
)
[2] => Array(
"id" => 3,
"latitude" => "12.541561",
"longitude", => "32.215",
"content", => "Another content"
)
)
Basically I want to concatenate the value in content(key) if the id(key) is the same with other id(key) value.
Any help would be greatly appreciated.
Try this -
$array = array();
foreach ($yourArray as $val) {
if (!array_key_exists($val['id'], $array)) {
$array[$val['id']] = $val;
} else {
$array[$val['id']]['content'] .= ' '.$val['content'];
}
}
hope this helps you :)
/**
* merge 2 arrays and return a new merged array. if same same key exists it will overwrite , unlike array_merge_recursive
* #param $a
* #param $b
* #return array|mixed
*/
public static function mergeArray($a,$b){
$args=func_get_args();
$res=array_shift($args);
while(!empty($args))
{
$next=array_shift($args);
foreach($next as $k => $v)
{
if(is_integer($k))
isset($res[$k]) ? $res[]=$v : $res[$k]=$v;
elseif(is_array($v) && isset($res[$k]) && is_array($res[$k]))
$res[$k]=self::mergeArray($res[$k],$v);
else
$res[$k]=. $v;
}
}
return $res;
}

Categories