I'm struggling to understand a problem that i've got with recursion where by I am traversing down a hiearchy in only one direction and adding all the children of each parent to an array.
However I can't quite work out the best way to continuously add to that array while using recursion.
Here is what my function looks like so far. I'd appreciate it hugely if someone could push me in the right direction with this.
function getTaxChildrenList($cat, $data){
$next = get_terms('location_types', 'orderby=count&hide_empty=0&parent='.$cat);
if(count($next)){
$result = array();
foreach($next as $next_key => $next_level){
$result[$next_level->term_id] = $this->getTaxChildrenList($next_level->term_id, $result);
}
}
return $result;
}
I now have the structure being returned as I want but need to actually add the objects of each of these children to the key which i'm guessing might be my base case.
Screenshot -> http://cl.ly/image/1z1v2S243f3H
So perhaps you need something like this:
function getTaxChildrenList($cat){
$next = get_terms('location_types', 'orderby=count&hide_empty=0&parent='.$cat);
$result = array();
if(count($next)){
foreach($next as $next_key => $next_level){
$result[$next_level->term_id] =
array("data" => $next_level->something,
"children" => $this->getTaxChildrenList($next_level->term_id));
}
}
return $result;
}
Is it something like that?
This should do the job
function getTaxChildrenList($cat) { //no second parameter
$next = get_terms('location_types', 'orderby=count&hide_empty=0&parent='.$cat);
$result = array();
//base case would be count($next) == 0
if(count($next)){
foreach($next as $next_level){
//add recursively arrays
$recur = $this->getTaxChildrenList($next_level->term_id);
if(count($recur) == 0)
$result[$next_level->term_id] = $next_level;
else
$result[$next_level->term_id] = $recur;
}
}
return $result;
}
Related
I have an small piece of PHP code that needs to put every file in the current directory into an array.
I have done this by making reading the dir with glob() and when it meets another dir it will loop.
My code I have as of now:
<?php
$find = '*';
$result = array();
function find($find)
{
foreach (glob($find) as $entry)
{
$result[] = $entry;
echo $entry.'<br>';
if (is_dir($entry)){
$zoek = ''.$entry.'/*';
find($zoek);
}
}
return $result;
}
print_r(find($find));
?>
When I execute the code the echo print exactly what I want. But the printed array doesn't give me the values I want, it only gives the values in the first dir it will come by then it seems to stop adding the value in the array.
What am I doing wrong?
You need to actually preserve the results you produce in the recursive callings to your function:
<?php
function listNodesInFolder($find) {
$result = [];
foreach (glob($find) as $entry) {
$result[] = $entry;
if (is_dir($entry)) {
$result = array_merge($result, find($entry.'/*'));
}
}
return $result;
}
print_r(find('*'));
Once on it I also fixes a few other issues with your code:
$result should be declared as an array inside your function, that that even if it does not loop you still return an array and not something undefined.
indentation and location of brackets got adjusted to general coding standards, that makes reading your code much easier for others. Get used to those standards, it pays out, you will see.
no need for an extra variable for the search pattern inside the conditional.
a speaking name for the function that tells what it actually does.
you should not name variables and functions alike ("find").
You need to add the result of find() to the array
Edit added array_merge - Cid's idea
<?php
$find = '*';
function find($find)
{
$result = array();
foreach (glob($find) as $entry)
{
$result[] = $entry;
echo $entry.'<br>';
if (is_dir($entry)){
$zoek = ''.$entry.'/*';
$result = array_merge($result, find($zoek));
}
}
return $result;
}
print_r(find($find));
?>
I found this topological sorting function for PHP:
Source: http://www.calcatraz.com/blog/php-topological-sort-function-384/
function topological_sort($nodeids, $edges) {
$L = $S = $nodes = array();
foreach($nodeids as $id) {
$nodes[$id] = array('in'=>array(), 'out'=>array());
foreach($edges as $e) {
if ($id==$e[0]) { $nodes[$id]['out'][]=$e[1]; }
if ($id==$e[1]) { $nodes[$id]['in'][]=$e[0]; }
}
}
foreach ($nodes as $id=>$n) { if (empty($n['in'])) $S[]=$id; }
while (!empty($S)) {
$L[] = $id = array_shift($S);
foreach($nodes[$id]['out'] as $m) {
$nodes[$m]['in'] = array_diff($nodes[$m]['in'], array($id));
if (empty($nodes[$m]['in'])) { $S[] = $m; }
}
$nodes[$id]['out'] = array();
}
foreach($nodes as $n) {
if (!empty($n['in']) or !empty($n['out'])) {
return null; // not sortable as graph is cyclic
}
}
return $L;
}
I looks nice and short. Anyways, for some input - I get duplicate lines in the output - see http://codepad.org/thpzCOyn
Generally the sorting seems to be correct if I remove the duplicates with array_unique()
I checked the function with two examples and the sorting itself looks correct.
Should I just call array_unique() on the result?
I'm the author of the original topological sorting function. Thanks to Alex for bringing the duplicate edge issue to my attention. I've updated the function to correctly remove duplicate edges and nodes. The updated version is here:
http://www.calcatraz.com/blog/php-topological-sort-function-384 (Same as original link)
I added the following to achieve the deduplication:
// remove duplicate nodes
$nodeids = array_unique($nodeids);
// remove duplicate edges
$hashes = array();
foreach($edges as $k=>$e) {
$hash = md5(serialize($e));
if (in_array($hash, $hashes)) { unset($edges[$k]); }
else { $hashes[] = $hash; };
}
I had to serialize the edges to make sure duplicates were removed correctly. I also tidied the rest of the function up a bit and added some comments.
You get duplicate lines because there are duplicate edges. I'm no graph theory thug but I'm pretty sure this is not legal :
0 =>
array (
0 => 'nominal',
1 => 'subtotal',
),
2 =>
array (
0 => 'nominal',
1 => 'subtotal',
),
...
You can either add a test in the part that constructs the nodes, something like this :
if ($id==$e[0] && !in_array($e[1], $nodes[$id]['out']))
{
$nodes[$id]['out'][]=$e[1];
}
if ($id==$e[1] && !in_array($e[0], $nodes[$id]['in'])) // Not needed but cleaner
{
$nodes[$id]['in'][]=$e[0];
}
... or just make sure you don't pass duplicate edges to the function. :P
TL;DR
I have this data: var_export and print_r.
And I need to narrow it down to: http://pastebin.com/EqwgpgAP ($data['Stock Information:'][0][0]);
How would one achieve it? (dynamically)
I'm working with vTiger 5.4.0 CRM and am looking to implement a function that would return a particular field information based on search criteria.
Well, vTiger is pretty weakly written system, looks and feels old, everything comes out from hundreds of tables with multiple joins (that's actually not that bad) etc., but job is job.
The need arose from getting usageunit picklist from Products module, Stock Information block.
Since there is no such function as getField();, I am looking forward to filter it out from Blocks, that is actually gathering the information about fields also.
getBlocks(); then calls something close to getFields();, that again something close to getValues(); and so on.
So...
$focus = new $currentModule(); // Products
$displayView = getView($focus->mode);
$productsBlocks = getBlocks($currentModule, $displayView, $focus->mode, $focus->column_fields); // in theory, $focus->column_fields should/could be narrowed down to my specific field, but vTiger doesn't work that way
echo "<pre>"; print_r($productsBlocks); echo "</pre>"; // = http://pastebin.com/3iTDUUgw (huge dump)
As you can see, the array under the key [Stock Information:], that actually comes out from translations (yada, yada...), under [0][0] contains information for usageunit.
Now, I was trying to array_filter(); the data out from there, but only thing I've managed to get is $productsBlocks stripped down to only contain [Stock Information:] with all the data:
$getUsageUnit = function($value) use (&$getUsageUnit) {
if(is_array($value)) return array_filter($value, $getUsageUnit);
if($value == 'usageunit') return true;
};
$productsUsageUnit = array_filter($productsBlocks, $getUsageUnit);
echo "<pre>"; print_r($productsUsageUnit); echo "</pre>"; // = http://pastebin.com/LU6VRC4h (not that huge of a dump)
And, the result I'm looking forward to is http://pastebin.com/EqwgpgAP, that I've manually got by print_r($productsUsageUnit['Stock Information:'][0][0]);.
How do I achieve this? (dynamically...)
function helper($data, $query) {
$result = array();
$search = function ($data, &$stack) use(&$search, $query) {
foreach ($data as $entry) {
if (is_array($entry) && $search($entry, $stack) || $entry === $query) {
$stack[] = $entry;
return true;
}
}
return false;
};
foreach ($data as $sub) {
$parentStack = array();
if ($search($sub, $parentStack)) {
$result[] = $parentStack[sizeof($parentStack) - 2];
}
}
return $result;
}
$node = helper($data, 'usageunit');
print_r($node);
Let me first start off, sorry for the confusing title. I didn't know how to exactly describe it but here goes. So I am querying a database for string. If there is only 1 result found then it is relatively easy to create an array, fill it with information, encode JSON and return that. I am confused as to when there are multiple results. The code below is what I am using but I highly doubt it is correct. I can't encode it into JSON format using my method which is what I need. If you can help at least point me in the correct direction, I would be more than grateful! Thank you!
PHP:
if ($action == 'profile') {
while ($pson = mysql_fetch_array($personQuery)) {
$typeSearch = 'profile';
$profQuery = mysql_query("SELECT * FROM tableName WHERE ColumnName LIKE '$query'");
$compQuery = mysql_query("SELECT * FROM tableName2 WHERE ColumnName LIKE '$query'");
if ($profQuery && mysql_num_rows($profQuery) > 0) {
$personQueryRows = mysql_num_rows($profQuery);
while ($row = mysql_fetch_array($profQuery)) {
if ($compQuery && mysql_num_rows($compQuery) > 0) {
while ($com = mysql_fetch_array($compQuery)) {
if (mysql_num_rows($profQuery) > 1) {
$compQueryRows = mysql_num_rows($compQuery);
if ($compQueryRows > 0) {
$compReturn = "true";
} else {
$compReturn = "false";
}
$nameArray = Array(
"success"=>"true",
"date"=>date(),
"time"=>$time,
"action"=>$action,
"returned"=>"true"
);
global $result;
for ($i=1;$i<=$personQueryRows;$i++) {
$nameResult[$i]=Array(
"id"=>$row['id'],
"name"=>$row['name'],
"gender"=>$row['gender'],
"comp"=>$row['company'],
"queryType"=>"profile"
);
$result = array_merge($nameArray, $nameResult[$i]);
}
$encodedJSON = json_encode($result);
echo $encodedJSON;
}
}
}
}
}
}
}
}
Returned JSON:
{"success":"true","date":"Jun 29 2012","time":"14:43:16","action":"profile","returned":"true","id":"14321","name":"John Smith","gender":"male","comp":"ABC Studios, LLC.","queryType":"profile"}
{"success":"true","date":"Jun 29 2012","time":"14:43:16","action":"profile","returned":"true","id":"292742","name":"John Smith","gender":"male","comp":"DEF Studios, LLC.","queryType":"profile"}
JavaScript error (when parsing JSON):
Uncaught SyntaxError: Unexpected token {
P.S. I am just getting started with PHP Arrays, and JSON formatting so I apologize if this is totally wrong. Still in the learning phase.
It looks like you're building up $nameResult[$i], but then you do:
$result = array_merge($nameArray, $nameResult[$i]);
You're doing that in each iteration of that for loop (once for each of the rows you got back), meaning that each time, you're clobbering $result.
After you finish that for loop, you then take whatever $result finally is (meaning the last $personQueryRows), and then json_encode it.
Looking at your other question (http://stackoverflow.com/questions/11257490/jquery-parse-multidimensional-array), it looks like what you should really be doing is before the loop where you go over $personQueryRows:
$output=$nameArray;
And then replace the array_merge line with:
$output[] = $nameResult[$i];
That last line will append the $result array onto the $output array as a new array member, meaning that it's nesting the array, which is what you'll need for your nested JSON.
Your code should look like this:
global $result;
$result = array();
.......
if ($action == 'profile') {
while{{{{{{{{...}}}}}}}}}}}
$encodedJSON = json_encode( $result );
echo $encodedJSON;
}
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.