Count all values in nested arrays using recursion in PHP - php

I want to learn how to use recursive functions, so I started to create one:
<?php
$tableau = [[[[2],[2]],[[2],[2]]],[[[2],[2]],[[2],[2]]]];
function test_count_recursive($tab, $compte = 0){
foreach($tab as $ss_tab){
if(!is_array($ss_tab)){
$compte += 1;
}
else{
test_count_recursive($ss_tab, $compte);
}
}
return $compte;
}
echo test_count_recursive($tableau);
But it doesn't work, can you tell me why?

Generally, you pass a piece of data up the recursive call tree or down, not both. In this case, you don't need to pass parent values down. Just return the count and let the parent node accumulate it:
<?php
function test_count_recursive($tab /* one param only */) {
$compte = 0; // <-- store the local total for this node
foreach ($tab as $ss_tab) {
if (is_array($ss_tab)) {
$compte += test_count_recursive($ss_tab /* one param only */);
// ^^^^^^^ accumulate total from children
}
else {
$compte++;
}
}
return $compte;
}
$tableau = [[[[2],[2]],[[2],[2]]],[[[2],[2]],[[2],[2]]]];
echo test_count_recursive($tableau); // => 8

Related

PHP mysql Tree Child count and List All Child node level wise

I have a tree like
and I want the output for requeted ID for example Admin
my table structure is
I have a method that returns Child count level wise but I also want to return the child list level-wise with child count level-wise like 2nd image output required.
function childCountLevelWise($conn,$ID, $level){
if ($level>14){
$count = array(0=>0);
return $count;
}
$sql="select * from user_my_tree t where t.parent_ID=".$ID;
$result=returnResults($conn,$sql);
if ($result==null){
$count = array(0=>0);
}
else{
$count = array(0=>0);
foreach($result as $key=>$row)
{
$count[0]++;
$children=childCountLevelWise($conn,$row['ID'], $level+1);
$index=1;
foreach ($children as $child)
{
if ($child==0)
continue;
if (isset($count[$index]))
$count[$index] += $child;
else
$count[$index] = $child;
$index++;
}
}
}
return $count;
}
It sounds like you just want a way of counting nested sets, I can reproduce your example without using a database with this code:
function returnResults($parent)
{
switch ($parent) {
case 'root':
return ['vijay', 'suresh', 'mukesh'];
case 'vijay':
return ['manish', 'rohan', 'manu'];
case 'manish':
return ['rinku', 'raja', 'vijay2'];
default:
return [];
}
}
function childCountLevelWise($parent, $level, &$result)
{
$result[$level] = empty($result[$level]) ? [] : $result[$level]; // init array
if ($level > 14) {
return; // ignore levels over 14
}
$levelResults = returnResults($parent); // get results for this parent
$result[$level] = array_merge($result[$level], $levelResults); // add to results for this level
foreach ($levelResults as $child) {
childCountLevelWise($child, $level + 1, $result); // check for each child at this level
}
}
And calling it and printing the results with this code
childCountLevelWise('root', 0, $result);
// print result
foreach ($result as $level => $people) {
if (!empty($people)) {
printf('Result for level %d: %s', $level, implode(',', $people));
echo "\n";
}
}
Will result in:
Result for level 0: vijay,suresh,mukesh
Result for level 1: manish,rohan,manu
Result for level 2: rinku,raja,vijay2
From there I think it should be simple enough to modify the returnResults function in my example to query the database, although if you're using this on a lot of results you might want to consider the performance costs of this. There are good solutions to having tree structures in the database already, such as the Nested Set in Doctrine.

How to replace huge array with PHP generators

Seeking your help to convert one big array with PHP generators.
Below is my code for which I need rework:
I am getting a result set from a service call and assigning all to an array:
foreach ($objects->result as $pointStdObject) {
$pointStdObjects[] = $pointStdObject;
}
This piece of code is inside a while loop which queries for records with an offset of 1000.
Issue is $pointStdObjects[] tends to get very huge and I get PHP out of memory exception.
Later I again need to use this same array as:
foreach ($pointStdObjects as $pointStdObject) {
$point = $this->pointFactory->createPointFromStdObject($pointStdObject);
if (!$point) {
continue;
}
$points[] = $point;
}
return $points;
Please suggest if we can leverage PHP generators or yield here
function getStd()
{
///your code before that
foreach ($objects->result as $pointStdObject) {
yield $pointStdObject;
}
}
function useStd()
{
foreach (getStd() as $pointStdObject) {
$point = $this->pointFactory->createPointFromStdObject($pointStdObject);
if (!$point) {
continue;
}
$points[] = $point;
}
return $points;
}

How to find length of the return type in recursive function

While going through the code, it written for DFS something like I have shown below.
I want to get the length of the each traversal , how can I use the static variable to get the return value,
<?php
.....
.....
while loop start
{
//
// some code
//
$deletebranch = json_encode($todel);
echo $deletebranch;
foreach ($abc as $key => $xyz)
{
///
/// code for finding $letter, $array_node
///
$finaldepthsearchgraph[$letter]['vertex'] = $letter;
$finaldepthsearchgraph[$letter]['visited'] = false;
$finaldepthsearchgraph[$letter]['letter'] = $letter;
$finaldepthsearchgraph[$letter]['neighbours'] = $array_node;
///
///
}
depthFirstSearch($finaldepthsearchgraph['A'], $finaldepthsearchgraph);
} // end of while loop
function depthFirstSearch($vertex, $list )
{
if (!$vertex['visited']) {
echo $vertex['letter'];
//var_dump($list);
// output on screen
// mark vertex as visited
$list[$vertex['vertex']]['visited'] = true;
foreach ($vertex['neighbours'] as $neighbour) {
// Watch neighbours, which were not visited yet
if (!$list[$neighbour]['visited']) {
// going through neighbour-vertexes
$list = depthFirstSearch(
$list[$neighbour],
$list
);
}
}
}
return $list;
}
....
?>
ANSWER
The Out Put I am getting is, I need to find the length to use it later in the code, please dont suggest any other DFS method, I have to carry through this only.
["A","B"]
ACBFGHL
["B","C"]
ABCFGHL
["A","C"]
ABCFGHL
["C","F"]
ABC
["F","G"]
ABCF
["G","H"]
ABCFGLH
["H","L"]
ABCFGHL
["G","L"]
ABCFGHL

loop problem with goto in php

I have programmed a script with the goto command but on the server where I want to execute the script there is a previous PHP version (<5.3), so I have to change the code. The structure of the code goes like that:
for($i = 0; $i < 30; $i++) // print 30 articles
{
$x = 0;
// choose a a feed from the db
// parse it
a:
foreach($feed->get_items($x, 1) as $item)
{
// create a unique id for the article of the feed
if($id == $dbid)
{
// if this id exists in the db, take the next article of the same feed which is not in the db
$x++;
goto a;
}
else
{
// print the original article you grabbed
}
} // end of foreach
} // end of for
I have tested everything. Do you have any ideas how can I retransform this code without goto in order to be executed properly???
This questions demonstrates why goto should be avoided. It lets you get away without thinking about the algorithm enough.
The standard way to do this is with a flag. I hope you were not expecting a "herezthecode kthxbai" sort of an answer, but in this case the best way to explain it would be to write the code -
for($i=0;$i<30;$++){
$x=0;
do {
$found = false;
foreach($feed->get_items($x,1) as $item){
// get $id
if($id==$dbid){
$found = true;
break;
}else{
// other things
}
}
$x++;
} while($found);
}
Without knowing how the ->get_items() call behaves you could use this brute-force method in lieu of the goto-switch:
for($i = 0; $i < 30; $i++)
{
$x = 0;
$a = 1;
while ($a--)
foreach($feed->get_items($x, 1) as $item)
{
if($id == $dbid)
{
$x++;
$a=1; break;
}
else
{
}
} // end of foreach
} // end of for
The label gets replaced by a while and a self-fulfulling stop condition. And the goto becomes a break and resets the $a stop condition.
Something like this would probably work...
function loop(){
foreach($feed->get_items($x,1) as $item){
if($id==$dbid){
$x++;
loop();
}else{
}
}
}
for($i=0;$i<30;$++){
$x=0;
loop();
}
I'm sorry, I removed all the comments, they were annoying.
Move the declaration of $x outside of the for loop and replace your label/goto combination with a break, like so...
$x=0;
for($i=0;$i<30;$++) //print 30 articles
{
foreach($feed->get_items($x,1) as $item)
{
// create a unique id for the article of the feed
if($id==$dbid)
{
//if this id exists in the db,take the next article of the same feed which is not in the db
$x++;
continue;
}
else
{
//print the original article you grabbed
}
} // end of foreach
}//end of for
Agree with unset - using break will break if loop and keep iterating through for loop

How do I store the results of this recursive function?

I have the following PHP code which works out the possible combinations from a set of arrays:
function showCombinations($string, $traits, $i){
if($i >= count($traits)){
echo trim($string) . '<br>';
}else{
foreach($traits[$i] as $trait){
showCombinations("$string$trait", $traits, $i + 1);
}
}
}
$traits = array(
array('1','2'),
array('1','2','3'),
array('1','2','3')
);
showCombinations('', $traits, 0);
However, my problem is that I need to store the results in an array for processing later rather than just print them out but I can't see how this can be done without using a global variable.
Does anyone know of an alternative way to achieve something similar or modify this to give me results I can use?
Return them. Make showCombinations() return a list of items. In the first case you only return one item, in the other recursive case you return a list with all the returned lists merged. For example:
function showCombinations(...) {
$result = array();
if (...) {
$result[] = $item;
}
else {
foreach (...) {
$result = array_merge($result, showCombinations(...));
}
}
return $result;
}
In addition to the other answers, you could pass the address of an array around inside your function, but honestly this isn't nearly the best way to do it.
Using the variable scope modifier static could work. Alternatively, you could employ references, but that's just one more variable to pass. This works with "return syntax".
function showCombinations($string, $traits, $i){
static $finalTraits;
if (!is_array($finalTraits)) {
$finalTraits = array();
}
if($i >= count($traits)){
//echo trim($string) . '<br>';
$finalTraits[] = $string;
} else {
foreach($traits[$i] as $trait){
showCombinations("$string$trait", $traits, $i + 1);
}
}
return $finalTraits;
}
$traits = array(
array('1','2'),
array('1','2','3'),
array('1','2','3')
);
echo join("<br>\n",showCombinations('', $traits, 0));
Of course, this will work as expected exactly once, before the static nature of the variable catches up with you. Therefore, this is probably a better solution:
function showCombinations($string, $traits, $i){
$finalTraits = array();
if($i >= count($traits)){
$finalTraits[] = $string;
} else {
foreach($traits[$i] as $trait){
$finalTraits = array_merge(
$finalTraits,
showCombinations("$string$trait", $traits, $i + 1)
);
}
}
return $finalTraits;
}
although the solution by Lukáš is the purest as it has no side effects, it may be ineffective on large inputs, because it forces the engine to constantly generate new arrays. There are two more ways that seem to be less memory-consuming
have a results array passed by reference and replace the echo call with $result[]=
(preferred) wrap the whole story into a class and use $this->result when appropriate
the class approach is especially nice when used together with php iterators
public function pageslug_genrator($slug,$cat){
$page_check=$this->ci->cms_model->show_page($slug);
if($page_check[0]->page_parents != 0 ){
$page_checks=$this->ci->page_model->page_list($page_check[0]->page_parents);
$cat[]=$page_checks['re_page'][0]->page_slug;
$this->pageslug_genrator($page_checks['re_page'][0]->page_slug,$cat);
}
else
{
return $cat;
}
}
this function doesnt return any value but when i m doing print_r $cat it re
store the results in a $_SESSION variable.

Categories