update a global array within a function - php

I have a global array in which I am trying to update a value (remain persistent) and then display it.
I get no error but the array never gets updated.
<?php
$anchor = 'bird';
$stuff = array('apple', 'bird', 'frog');
function insert($anchor, $stuff){
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
}
for($x = 0; $x < 2; $x++){
insert($anchor, $stuff);
var_dump($stuff);
}
output:
array(3) {
[0]=>
string(5) "apple"
[1]=>
string(4) "bird"
[2]=>
string(4) "frog"
}
array(3) {
[0]=>
string(5) "apple"
[1]=>
string(4) "bird"
[2]=>
string(4) "frog"
}
expected output:
{'bird' => 674568765}
{'bird' => 986266261}
How do I update an array within a function, so that the changes are reflected globally (persistent)?

Pass the variable $stuff by reference. Note the & in the function parameters.
function insert($anchor, &$stuff){ // note the & mark
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
}

As others have mentioned: by default, PHP function arguments are passed by value, meaning that a value changed inside a function does not change outside of the function.
I suggest returning the new $stuff value from your function:
<?php
$anchor = 'bird';
$stuff = array('apple', 'bird', 'frog');
function insert($anchor, $stuff){
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
return $stuff;
}
for($x = 0; $x < 2; $x++){
$stuff=insert($anchor, $stuff);
}
echo"<pre>".print_r($stuff,true)."</pre>";
?>
Array
(
[0] => apple
[1] => bird
[2] => frog
[3] => Array
(
[bird] => 618490127
)
[4] => Array
(
[bird] => 1869073273
)
)
Other solutions propose passing by reference, to which I'm not opposed. But I have definitely run into buggy code in which it wasn't immediately clear that a function was changing a value, and I've been confused by unexpected loop behavior. As a result, I generally prefer arguably more readable/maintainable code that returns a new value.
See When to pass-by-reference in PHP.
Get technical with Sara Golemon.

If you want changes to a variable that you pass to a function to persist after the function ends, pass the variable by reference:
Change:
function insert($anchor, $stuff)
To
function insert($anchor, &$stuff)

Related

Generate multidimensional array based on given array

I have an array say,
$arr = ["x", "y", "z"];
What I want to achieve is create another array based on given array such as
$arr1["x" =>["y" => ["z"]]] = "some value";
Any idea to achieve this? Thanks in advance.
Edited:
'some value' is just a dummy data. What I'm trying to achieve is the multidimensional structure.
You can recursively build an array, taking and removing the first element of an array on each call :
function buildArray($arr, $someValue)
{
if (count($arr) == 0)
return $someValue;
// the key is the first element of the array,
// removed and returned at the same time using array_shift()
return [ array_shift($arr) => buildArray($arr, $someValue) ];
}
$arr = ["x", "y", "z"];
$arr1 = buildArray($arr, "some value");
var_dump($arr1);
echo "------------------------" . PHP_EOL;
// note that $arr is preserved
var_dump($arr);
This outputs :
array(1) {
["x"]=>
array(1) {
["y"]=>
array(1) {
["z"]=>
string(10) "some value"
}
}
}
------------------------
array(3) {
[0]=>
string(1) "x"
[1]=>
string(1) "y"
[2]=>
string(1) "z"
}
$keys = array('key1', 'key2', 'key3');
$value = 'some value';
$md = array();
$md[$keys[count($keys)-1]] = $value;
for($i=count($keys)-2; $i>-1; $i--)
{
$md[$keys[$i]] = $md;
unset($md[$keys[$i+1]]);
}
print_r($md);
You need to create recursive function:
function rec_arr($ar, $val){
$res = [];
if(is_array($ar) && count($ar)>0){
$tmp = $ar[0]; // catching the first value
unset($ar[0]); // unset first value from given array
sort($ar); // makes indexes as 0,1,...
$res[$tmp] = rec_arr($ar, $val); // recursion
} else {
return $val; // passing value to the last element
}
return $res;
}
Demo
Outputs:
Array
(
[x] => Array
(
[y] => Array
(
[z] => some value
)
)
)

How to Seperate Array with equals to and create single array

Hi I am working on very complex array operations.
I have $temp variable which stores pipe separated string like Height=10|Width=20
I have used explode function to convert into array and get specific output.
Below code i have try :
$product_attributes = explode("|",$temp)
//below output i get after the explode.
$product_attributes
Array(
[0]=>Height=10
[1]=>width=20
)
But i want to parse this array to separate one.
My expected output :
Array (
[0]=>Array(
[0] => Height
[1] => 10
)
[1]=>Array(
[0]=>Width
[1]=>20
)
)
Which function i need to used to get the desire output ?
Before downvoting let me know if i have made any mistake
You could try the below code. I've tested this and it outputs the result you've shown in your post.
$temp = 'Height=10|Width=20';
$product_attributes = explode('|', $temp);
$product_attributes2 = array();
foreach ($product_attributes as $attribute) {
$product_attributes2[] = explode('=', $attribute);
}
print_r($product_attributes2);
Try Below code
<?php
$temp = "Height=10|Width=20";
$product_attributes = explode("|", $temp);
foreach ($product_attributes as $k => $v) {
$product_attributes[$k] = explode('=', $v);
}
echo '<pre>';
print_r($product_attributes);
?>
check running answer here
Process your result by this:
$f = function($value) { return explode('=', $value); }
$result = array_map($f, $product_attributes);
One more option is to split the values in to one array and then build them from there.
$str = "Height=10|Width=20";
$arr = preg_split("/\||=/", $str);
$arr2= array();
$j=0;
for($i=0;$i<count($arr);$i++){
$arr2[$j][]= $arr[$i];
$arr2[$j][]= $arr[$i+1];
$i++;
$j++;
}
var_dump($arr2);
The output will be:
$arr = array(4){
0 => Height
1 => 10
2 => Width
3 => 20
}
$arr2 = array(2) {
[0]=>
array(2) {
[0]=>
string(6) "Height"
[1]=>
string(2) "10"
}
[1]=>
array(2) {
[0]=>
string(5) "Width"
[1]=>
string(2) "20"
}
}

remove duplicates from an array based on object property continued

related to : Remove duplicates from an array based on object property?
is there a way to do this exact same thing but consider the order? for example if instead of keeping [2] I wanted to keep [1]
[0]=>
object(stdClass)#337 (9) {
["term_id"]=>
string(2) "23"
["name"]=>
string(12) "Assasination"
["slug"]=>
string(12) "assasination"
}
[1]=>
object(stdClass)#44 (9) {
["term_id"]=>
string(2) "14"
["name"]=>
string(16) "Campaign Finance"
["slug"]=>
string(16) "campaign-finance"
}
[2]=>
object(stdClass)#298 (9) {
["term_id"]=>
string(2) "15"
["name"]=>
string(16) "Campaign Finance"
["slug"]=>
string(49) "campaign-finance-good-government-political-reform"
}
I tried posting a comment but because of my reputation it won't let me so I decided to start a new thread
It is trivial to reverse and array. So before processing the array, call array_reverse() on it:
/** flip it to keep the last one instead of the first one **/
$array = array_reverse($array);
Then at the end you can reverse it again if ordering is an issue:
/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);
So put all together looks like this:
class my_obj
{
public $term_id;
public $name;
public $slug;
public function __construct($i, $n, $s)
{
$this->term_id = $i;
$this->name = $n;
$this->slug = $s;
}
}
$objA = new my_obj(23, "Assasination", "assasination");
$objB = new my_obj(14, "Campaign Finance", "campaign-finance");
$objC = new my_obj(15, "Campaign Finance", "campaign-finance-good-government-political-reform");
$array = array($objA, $objB, $objC);
echo "Original array:\n";
print_r($array);
/** flip it to keep the last one instead of the first one **/
$array = array_reverse($array);
/** Answer Code begins here **/
// Build temporary array for array_unique
$tmp = array();
foreach($array as $k => $v)
$tmp[$k] = $v->name;
// Find duplicates in temporary array
$tmp = array_unique($tmp);
// Remove the duplicates from original array
foreach($array as $k => $v) {
if (!array_key_exists($k, $tmp))
unset($array[$k]);
}
/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);
echo "After removing duplicates\n";
echo "<pre>".print_r($array, 1);
produces the following output
Array
(
[0] => my_obj Object
(
[term_id] => 23
[name] => Assasination
[slug] => assasination
)
[1] => my_obj Object
(
[term_id] => 15
[name] => Campaign Finance
[slug] => campaign-finance-good-government-political-reform
)
)
Here is one way to do it. This should keep the first object for each name attribute and remove the rest.
$unique = [];
foreach ($array as $key => $object) {
if (!isset($unique[$object->name])) {
// this will add each name to the $unique array the first time it is encountered
$unique[$object->name] = true;
} else {
// this will remove all subsequent objects with that name attribute
unset($array[$key]);
}
}
I used the object name as a key rather than a value in the $unique array so isset could be used to check for existing names, which should be faster than in_array which would have to be used if the names were added as values.
You can do it with array_reduce:
$names = [];
$withUniqueNames = array_reduce(
[$objA, $objB, $objC],
function ($withUniqueNames, $object) use (&$names) {
if (!in_array($object->name, $names)) {
$names[] = $object->name;
$withUniqueNames[] = $object;
}
return $withUniqueNames;
},
[]
);
Basically, we traverse an array of objects and keep track of used names. If the name was not used yet, we add it to used and add current object to the result.
Here is working demo.

Passing/Returning references to object + changing object is not working

I am using an answer from How to get random value out of an array to write a function that returns a random item from the array. I modified it to pass by reference and return a reference.
Unfortunately, it doesn't appear to work. Any modifications to the returned object do not persist. What am I doing wrong?
I'm on PHP 5.4 if that makes a difference (Don't ask).
function &random_value(&$array, $default=null)
{
$k = mt_rand(0, count($array) - 1);
$return = isset($array[$k])? $array[$k]: $default;
return $return;
}
Usage...
$companies = array();
$companies[] = array("name" => "Acme Co", "employees"=> array( "John", "Jane" ));
$companies[] = array("name" => "Inotech", "employees"=> array( "Bill", "Michael" ));
$x = &random_value($companies);
$x["employees"][] = "Donald";
var_dump($companies);
Output...
array(2) {
[0] =>
array(2) {
'name' =>
string(7) "Acme Co"
'employees' =>
array(2) {
[0] =>
string(4) "John"
[1] =>
string(4) "Jane"
}
}
[1] =>
array(2) {
'name' =>
string(7) "Inotech"
'employees' =>
array(2) {
[0] =>
string(4) "Bill"
[1] =>
string(7) "Michael"
}
}
}
I even copied and pasted the examples from the documentation and none of those worked either. They all output null.
The ternary operator creates an implicit copy, which breaks the reference chain. Use an explicit if... else:
function &random_value(&$array, $default=null)
{
$k = mt_rand(0, count($array) - 1);
if (isset($array[$k])) {
return $array[$k];
} else {
return $default;
}
}
As to why, the docs now state:
Note: Please note that the ternary operator is an expression, and that it doesn't evaluate to a variable, but to the result of an expression. This is important to know if you want to return a variable by reference. The statement return $var == 42 ? $a : $b; in a return-by-reference function will therefore not work and a warning is issued in later PHP versions.
See also this bug where the ternary operator actually returns by reference in a foreach context, when it shouldn't.
I'm having trouble wrapping my head around a better function aspect than #bishop as I have just consumed an enormous lunch, however this works:
$x =& $companies[array_rand($companies)];
$x["employees"][] = "Donald";
var_dump($companies);

Turn flat array into nested array

Just working on something and can't find a simple solution to this problem without just looping through the array with a foreach. Does anyone have a simple solution for this
I want to turn this
&array(4) {
["a"]=>int(0)
["b"]=>int(1)
["c"]=>int(2)
["d"]=>int(3)
}
Into this
array(1) {
["a"]=>
array(1) {
[0]=>
array(1) {
["b"]=>
array(1) {
[0]=>
array(1) {
["c"]=>
array(1) {
[0]=>
array(1) {
["d"]=> int(1) //this value does not matter
}
}
}
}
}
}
}
The values don't matter at all I just need the keys to run against a array_intersect_key_recursive function that I have.
EDIT : The array has changed after testing it needs to be nested as an array of an array
I don't know how this could possibly end up helping you, but it was a fun exercise nonetheless.
$newArray = array();
$last = &$newArray;
$array = array_reverse(array_keys($array));
while ($item = array_pop($array)) {
if (!is_array($last)) {
$last = array();
}
$last[$item] = array(array());
$last = &$last[$item][0];
}
NOTE: I made this answer with the int(1). You said the value is not important so I'm not going to bother changing it for now, but you would have to do a bit more work if the value was important (probably something like get the value from the original array with $item as the key).
Another approach using recursion:
function toNestedArray(array $array, $index = 0) {
$return = array();
if ($index < count($array)) {
$return[$array[$index]] = toNestedArray($array, ++$index);
}
return $return;
}
Usage Example:
$flatArray = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4);
$nestedArray = toNestedArray(array_keys($flatArray));
print_r($nestedArray);
Output:
Array
(
[a] => Array
(
[b] => Array
(
[c] => Array
(
[d] => Array
(
)
)
)
)
)

Categories