Since arrays are not treated as objects in php, I have to manually reference them in order to recursively fill an array or constantly copy the new created/filled arrays from recursive calls.
"return" myRecursiveFunc() is not an option because that would end the function at the wrong time.
One way: Referencing
public function depthFirstSearchInOrder()
{
$list = []; //otherwise php screams that only variables can be referenced (inside traverseInOrder)
//Otherwise I would have passed an empty [] as second parameter
return traverseInOrder($this->root, $list);
}
function traverseInOrder($node, &$list)
{
if ($node->left) {
traverseInOrder($node->left, $list);
}
$list[] = $node->value;
if ($node->right) {
traverseInOrder($node->right, $list);
}
return $list;
}
Second way: Creating/Copying arrays
public function depthFirstSearchInOrder()
{
return traverseInOrder($this->root);
}
function traverseInOrder($node, $list = [])
{
if ($node->left) {
$list = traverseInOrder($node->left, $list);
}
$list[] = $node->value;
if ($node->right) {
$list = traverseInOrder($node->right, $list);
}
return $list;
}
Doesn't the second way eat up way more space?
I'm creating new Arrays for each recursive call which I'm then copying again in $list.
Or am I just confused at the moment?
If I was to traverse this tree
// 9,
// 4, 20
// 1, 6, 15 170
Using the second way
I would start with 9
Go left and call recursive (because of 4)
Go left and call recursive (because of 1)
I fill my empty array with 1 and return the array [1]
I'm back to value 4 where I now assign to $list = [1] (which now is a copy of [1] NOT a reference, right? So the array [1] still exists afterwards somewhere?
I push 4 in the array [1,4] and go right (because of 6)
I push 6 to the array [1,4,6] and return this array [1,4,6]
I'm back to value 4 where I now assign to $list = [1,4,6] (which now is a copy of [1,4,6] NOT a reference, right? So the array [1,4,6] still exists afterwards somewhere?
And so on and so forth...
Back to my question:
Isn't the second way more space hungry? Or am I just confused at the moment?
Related
I have an array that contains any number of elements, and is allowed to be a multidimensional array, too. My testing example of such array data is:
$arr = array(
array('Material-A', 'Material-B'),
array('Profile-A', 'Profile-B', 'Profile-C'),
array('Thread-A', 'Thread-B'),
// ... any number of elements
);
From this multidimensional array I need to create a single array that is linear in the following format.
$arrFormated = array(
'Material-A',
'Material-A_Profile-A',
'Material-A_Profile-A_Thread-A',
'Material-A_Profile-A_Thread-B',
'Material-A_Profile-A_Thread-C',
'Material-A_Profile-B',
'Material-A_Profile-B_Thread-A',
'Material-A_Profile-B_Thread-B',
'Material-A_Profile-B_Thread-C',
'Material-A_Profile-C',
'Material-A_Profile-C_Thread-A',
'Material-A_Profile-C_Thread-B',
'Material-A_Profile-C_Thread-C',
'Material-B',
'Material-B_Profile-A',
'Material-B_Profile-A_Thread-A'
// Repeat similar pattern found above, etc...
);
For a recursive function, the best that I've been able to come up with thus far is as follows:
private function showAllElements($arr)
{
for($i=0; $i < count($arr); $i++)
{
$element = $arr[$i];
if (gettype($element) == "array") {
$this->showAllElements($element);
} else {
echo $element . "<br />";
}
}
}
However, this code is no where close to producing my desired results. The outcome from the above code is.
Material-A
Material-B
Profile-A
Profile-B
Profile-C
Thread-A
Thread-B
Could somebody please help me with the recursive side of this function so I may get my desired results?
I'd generally recommend thinking about what you want to be recursive. You tried to work with the current element in every recursion step, but your method needs to look at the next array element of the original Array in each recursion step. In this case, it's more useful to pass an index to your recursive function, because the 'current element' (the $arr in showAllElements($arr)) is not helpful.
I think this code should do it:
$exampleArray = array(
array('Material-A', 'Material-B'),
array('Profile-A', 'Profile-B', 'Profile-C'),
array('Thread-A', 'Thread-B','Thread-C'),
// ... any number of elements
);
class StackOverflowQuestion37823464{
public $array;
public function dumpElements($level = 0 /* default parameter: start at first element if no index is given */){
$return=[];
if($level==count($this->array)-1){
$return=$this->array[$level]; /* This is the anchor of the recursion. If the given index is the index of the last array element, no recursion is neccesarry */
}else{
foreach($this->array[$level] as $thislevel) { /* otherwise, every element of the current step will need to be concatenated... */
$return[]=$thislevel;
foreach($this->dumpElements($level+1) as $stringifyIt){ /*...with every string from the next element and following elements*/
$return[]=$thislevel.'_'.$stringifyIt;
}
}
}
return $return;
}
}
$test=new StackOverflowQuestion37823464();
$test->array=$exampleArray;
var_dump($test->dumpElements());
I would like to pass in an array that contains a list of directories to scan. I want to iterate over each directory and push its content into another array that I will print out, but for some reason my code is not working. The directories exist and the path is correct. I think it has something to do with how I'm using foreach.
These are the errors I'm getting:
Notice: Undefined index: C:\Users\john\Desktop\files\images\ in
C:\xampp\htdocs\test.php on line 6
Warning: scandir(): Directory name cannot be empty in
C:\xampp\htdocs\test.php on line 6
This is the code:
function test($dir = []) {
foreach($dir as $bar) {
$list = [];
array_push($list, scandir($dir[$bar]));
}
print_r($list);
}
test(["C:\Users\john\Desktop\files\images\\", "C:\Users\john\Desktop\files\images\autumn\\"]);
If anyone can think of a simpler way to do this, please don't hesitate to tell me.
You're on the right track. There are a few changes you need to make though.
function test($dir = []) {
$list = [];
foreach($dir as $bar) {
$list[] = scandir($bar);
}
print_r($list);
}
As noted by #BrianPoole you need to move the $list out of the foreach loop. By having it in the loop, the array is reset with each iteration, resulting in the final array having one element.
In addition, the foreach loop as explained above by #TimCooper does not operate the same as in JavaScript. If you really want to access the keys, you can use the following syntax:
foreach($dir as $key => $bar)
You would then use either $dir[$key] or $bar to access the directory value.
Finally, array_push is an additional function call that in your case is not needed. By simply adding [] PHP will push the new value onto the end of the array.
function test($dir) {
// DEFINE LIST OUT SIDE OF LOOP
$list = array();
// Run checks
if(count($dir) > 0) {
// Loop Through Directory
foreach($dir as $directory) {
// Push into list
array_push($list, array("scanned"=>$directory, "contents" => scandir($directory)));
}
}else {
// If no directories are passed return array with error
$list = array(
"error" => 1,
"message" => "No directories where passed into test()",
);
}
print_r($list);
}
This is how I would do it. It provides a couple checks and sets up the data so you can se it a bit more clear.
I'm tryin to merge an array that I have when it is being created through a function
I have a function, and it returns an array.
class myarray
{
public function getAr($id)
{
// mysql query
while($dd= $database->fetch(PDO::FETCH_ASSOC))
{
$data[] = $dd; //there's values in the array when its being populated through the function of the while loop
}
return $data;
}
public function get3($id)
{
// mysl query
while($dd= $database->fetch(PDO::FETCH_ASSOC))
{
$data[] = $dd; //there's values in the array when its being populated through the function of the while loop
}
return $data;
}
}
How come I tried to merge together the array:
$get = new myarray();
while($row = $fet->fetch(PDO::FETCH_ASSOC))
{
$arrayAr = $get->getAr($id);
$array3 = $get->get3($id);
$new_array = array_merge($arrayAr ,$array3); //this gives me the error
print_r($arrayAr); //displays array
}
print_r($arrayAr); //displays nothing, why is that?
It says that its not an array?
array_merge() [function.array-merge]: Argument #1 is not an array
But I can print_r($arrayAr); and its like an array inside the while loop, but it doesn't display anything outside of it?
when I tried this...
$new_array = array_merge((array)$arrayAr ,(array)$array3);
It doesn't display an error, but it isn't merged either.
Help?
Thanks
You $arrayAr is local variable and 2nd print_r() is used beyond of scope of his variable. If you need it available wider, "declare" it before foreach, with i.e. $arrayAr = array(); (or whatever value you want - it is not important in this case, yet array() makes code clear).
I use multidimensional arrays to store product attributes (well Virtuemart does, to be precise).
When I tried to echo the sub-arrays value, if the sub-array did not exist PHP threw:
Fatal error: Cannot use string offset
as an array
To get around this, I attempted to create a function to check on every array level if it is an actual array, and if it is empty (when trying on the whole thing at once such as: is_array($array['level1']['level2']['level3']), I got the same error if level1 or level2 are not actual arrays).
This is the function ($array contains the array to check, $array_levels is an array containing the names of the sub-arrays, in the order they should apper):
function check_md_array($array,$array_levels){
if(is_array($array)){
$dimension = null; //This will store the dimensions string
foreach($array_levels as $level){
$dimension .= "['" . $level . "']"; //Add the current dimension to the dimensions string
if(!is_array($array/* THE CONTENT OF $dimension SHOULD BE INSERTED HERE*/)){
return false;
}
}
return true;
}
}
How can I take the string contained in $dimensions, and insert it into the code, to be part of the statement?
Short of doing an eval, I don't think you can do it.
if(eval("!is_array($array".$dimension.")"))
return false
However, this is another way to do it
function check_md_array($array,$array_levels){
if(!is_array($array))
return false;
foreach($array_levels as $level){
if(!isset($array[$level]) || !is_array($array[$level]))
return false;
$array = $array[$level];
}
return true;
}
Ok. I've written a simple(ish) function to take an argument and return the same argument with the danger html characters replaced with their character entities.
The function can take as an argument either a string, an array or a 2D array - 3d arrays or more are not supported.
The function is as follows:
public function html_safe($input)
{
if(is_array($input)) //array was passed
{
$escaped_array = array();
foreach($input as $in)
{
if(is_array($in)) //another array inside the initial array found
{
$inner_array = array();
foreach($in as $i)
{
$inner_array[] = htmlspecialchars($i);
}
$escaped_array[] = $inner_array;
}
else
$escaped_array[] = htmlspecialchars($in);
}
return $escaped_array;
}
else // string
return htmlspecialchars($input);
}
This function does work, but the problem is that I need to maintain the array keys of the original array.
The purpose of this function was to make it so we could literally pass a result set from a database query and get back all the values with the HTML characters made safe. Obviously therefore, the keys in the array will be the names of database fields and my function at the moment is replacing these with numeric values.
So yeah, I need to get back the same argument passed to the function with array keys still intact (if an array was passed).
Hope that makes sense, suggestions appreciated.
You can use recursion rather than nesting loads of foreaches:
function html_safe($input) {
if (is_array($input)) {
return array_map('html_safe', $input);
} else {
return htmlspecialchars($input);
}
}
Ok I think I've figured this one out myself...
my foreach loops didn't have any keys specified for example they were:
foreach($array_val as $val)
instead of:
foreach($array_val as $key => $val)
in which case I could have preserved array keys in the output arrrays.