when does php foreach index increment? - php

$databaseObjects = Database::instance()->query('SELECT * FROM table');
foreach ($databaseObjects as $key => $value) {
echo "(key: $key, value: $value)";
if ($key + 1 < count($objects))
$nextObject = $objects[$key + 1];
}
Given an array of database objects from Kohana, the above loop will skip all the odd keys. The reason for this is because accessing $objects[$key+1] increments $key. I don't understand the reason behind this. Since when did the array access operator increment the key?

$objects[$key+1] does not increment $key
Your code is weird - you set $nextObject but then never use it.
Remember: array keys are NOT always in order such as 0,1,2,3,4,5!
For instance, this array:
<?php
$arr = array(
0 => "Zero",
2 => "Two",
5 => "Five"
);
?>
Would print out the following in your script:
(key: 0, value: Zero)(key: 2, value: Two)(key: 5, value: Five)
... and this array:
<?php
$arr = array(
5 => "Five"
2 => "Two",
0 => "Zero",
);
?>
Would print out the following in your script:
(key: 5, value: Five)(key: 2, value: Two)(key: 0, value: Zero)

$objects[$key + 1]; does not increment the $key, $key + 1 is not being assigned to $key. Moreover, even if $key was assigned $key + 1, this won't affect the $key variable in the next iteration since $key is local to the foreach and initialized every time the foreach iterate on the next element in the array.
However, since this is a foreach loop, the $key is nothing but the index (assuming this array is not an associative one). Therefore the $key will be incrementing starting from zero every time the loop iterate on an element.

It appears I left out the important detail that I am iterating over Kohana database objects. They must have overwritten the foreach iterator in a fashion such that when I perform an array access within the foreach loop,
$nextObject = $objects[$key + 1];
it will affect the $key on the next iteration.

Related

Why casting an array with (array) cause the array items to not be updated?

Why does casting $arr with (array) cause the array items to not be modified?
$arr = array(1, 2, 3, 4);
foreach ((array)$arr as &$value) {
$value = $value * 2;
}
$arr should now equal [2,4,6,8] but for some reason it still equals [1,2,3,4].
You are not modifying the original array, rather, the current looped iteration. If you wanted to modify the original array, you'd need to access the keys:
foreach ((array) $arr as $k => $v) {
$arr[$k] = $v * 2;
}
It is possible to update the original by 'passing by reference' as confirmed by #user2864740 and the example that they have provided.
Thirdly, as #user2864740 pointed out in the original comment chain, using (array) seems to cause it to create a new array.
Live Example
Repl

Check if two arrays are equal key value with same name

I have two arrays:
$arr1 = array(
1 => 250,
2 => 325,
3 => 741,
4 => 690
);
$arr2 = array(
1 => 110,
2 => 740,
3 => 1200,
4 => 500
);
I want to check if all $arr2 values are less than $arr1 values
There are now 2 keys [1] + [4] its less than $arr1 keys [1] + [4]
Without a foreach loop, I want to return true or false if any key from $arr2 is less than the same key from $arr1.
Here is one way to do it.
$result = (bool) array_filter(array_map(function($a, $b){
return $b < $a;
}, $arr1, $arr2));
The inner array_map returns true or false based on the comparison of corresponding values of $arr1 and $arr2. Then the outer array_filter reduces the result to only include true values. Casting the result to boolean will yield true if all values in $arr2 are greater than or equal to the corresponding $arr1 values (because array_filter will return an empty array), and false if any of them are less.
Keep in mind that avoiding a foreach loop is not more efficient for something like this. Both the array_map and array_filter functions will iterate the entire array they are given. If you use a foreach instead, you can break out of the loop as soon as you find an element that meets the condition you're looking for, which in this case would be the first iteration of the foreach loop.
Here is an example that does not use foreach() but most of us would use an iterator to work with array elements. You could use for() or while() loops, too.
<?php // demo/temp_samer.php
/**
* Compare array elements
*
* https://stackoverflow.com/questions/45422576/check-if-two-arrays-are-equal-key-value-with-same-name
*/
error_reporting(E_ALL);
echo '<pre>';
$arr1=array('1'=>250,'2'=>325,'3'=>741,'4'=>690);
$arr2=array('1'=>110,'2'=>740,'3'=>1200,'4'=>500);
if ($arr2[1] < $arr1[1]) echo PHP_EOL . "KEY 1 IS LOWER IN THE SECOND ARRAY";
if ($arr2[2] < $arr1[2]) echo PHP_EOL . "KEY 2 IS LOWER IN THE SECOND ARRAY";
if ($arr2[3] < $arr1[3]) echo PHP_EOL . "KEY 3 IS LOWER IN THE SECOND ARRAY";
if ($arr2[4] < $arr1[4]) echo PHP_EOL . "KEY 4 IS LOWER IN THE SECOND ARRAY";
This shows a way of thinking about the problem.
https://iconoun.com/demo/temp_samer.php
<?php // demo/temp_samer.php
/**
* Compare array elements
*
* https://stackoverflow.com/questions/45422576/check-if-two-arrays-are-equal-key-value-with-same-name
*/
error_reporting(E_ALL);
echo '<pre>';
$arr1=array('1'=>250,'2'=>325,'3'=>741,'4'=>690);
$arr2=array('1'=>110,'2'=>740,'3'=>1200,'4'=>500);
foreach ($arr2 as $key => $value)
{
if ($value < $arr1[$key]) echo PHP_EOL . "KEY $key IS LOWER IN THE SECOND ARRAY";
}

foreach loop and reference of &$value

Why is an empty foreach loop can change the result.
I have the following code:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
var_dump($variable);
The result I get is:
array (size=4)
0 => int 2
1 => int 3
2 => int 4
3 => &int 5
Now, when I add an empty foreach loop like this:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
foreach ($variable as $key => $value);
var_dump($variable);
I get this :
array (size=4)
0 => int 2
1 => int 3
2 => int 4
3 => &int 4
can someone explain me why the last element doesn't change when I add the second empty loop, and why there is a & infront of the last element?
At the end of the first loop, $value is pointing to the same place as $variable[3] (they are pointing to the same location in memory):
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
Even as this loop is finished, $value is still a reference that's pointing to the same location in memory as $variable[3], so each time you store a value in $value, this also overwrites the value stored for $variable[3]:
foreach ($variable as $key => $value);
var_dump($variable);
With each evaluation of this foreach, both $value and $variable[3] are becoming equal to the value of the iterable item in $variable.
So in the 3rd iteration of the second loop, $value and $variable[3] become equal to 4 by reference, then during the 4th and final iteration of the second loop, nothing changes because you're passing the value of $variable[3] (which is still &$value) to $value (which is still &$value).
It's very confusing, but it's not even slightly idiosyncratic; it's the code executing exactly as it should.
More info here: PHP: Passing by Reference
To prevent this behavior it is sufficient to add an unset($value); statement after each loop where it is used. An alternative to the unset may be to enclose the foreach loop in a self calling closure, in order to force $value to be local, but the amount of additional characters needed to do that is bigger than just unsetting it:
(function($variable){
foreach ($variable as $key => &$value) $value++;
})($variable);
This is a name collision: the name $value introduced in the first loop exists after it and is used in the second loop. So all assignments to it are in fact assignments to the original array. What you did is easier observed in this code:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
$value = 123; // <= here you alter the array!
var_dump($variable);
and you will see $variable[3] as 123.
One way to avoid this is, as others said, to unset ($value) after the loop, which should be a good practice as recommended by the manual. Another way is to use another variable in the second loop:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
foreach ($variable as $key => $val);
var_dump($variable);
which does not alter your array.
The last element of the array will remian even after the foreach loop ..So its needed to use unset function outside the loop ..That is
$variable = [1,2,3,4];
foreach ($variable as $key => &$value) {
$value++;
}
unset($value);
var_dump($variable);
The link to the manual can be found here http://php.net/manual/en/control-structures.foreach.php
As phil stated in the comments:
As mentioned in the manual, you should unset() references after use.
$variable = [1,2,3,4];
foreach ($variable as $key => &$value) {
$value ++;
}
unset($value);
foreach ($variable as $key => $value);
print_r($variable);
Will return:
Array
(
[0] => 2
[1] => 3
[2] => 4
[3] => 5
)
Example
Explanation
Taken from the foreach() manual. (See the big red box)
Reference of a $value and the last array element remain even after the
foreach loop. It is recommended to destroy it by unset().
It basically means: That the referenced value &$value and the last element/item in the array, which in this case is 4 remain the same. To counter-act this issue, you'll have to unset() the value after use, otherwise it will stay in the array as its original value (if that makes sense).
You should also read this: How does PHP 'foreach' actually work?
After loop you should unset this reference using:
unset($value);
So your whole code should work like this:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value) {
$value++;
}
unset($value);
var_dump($variable);
There is no point to put unset($value); inside the loop
Explanation - after loop $value is still set to the last element of array so you can use after your loop $value = 10; (before unset) and you will see that last element of array has been changed to 10. It seems that var_dump want to help us a bit in this case and shows there is reference for last element and of course when we use unset we have desired output of var_dump.
You could also look at the following script:
<?php
$array = [1, 2, 3, 4];
var_dump($array);
$x = &$array[2];
var_dump($array);
$x += 20;
unset($x);
var_dump($array);
?>
We don't use loop here and if reference is set to element of array, var_dump shows us this putting & before type of this element.
However if the above code we changed reference and set it this way $x = &$array; var_dump wouldn't show us any reference.
Also in the following code:
<?php
$x = 23;
$ref = &$x;
var_dump($x);
?>
var_dump() won't give us any hint.
Obligatory statement: References are evil!
Stepping through your code:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value++;
After the loop completes; $value is a reference to $variable[3] and thus has the value of int(4).
foreach ($variable as $key => $value);
At each iteration, $variable[3] gets assigned an element of $variable[<k>] where 0 <= k < 3. At the last iteration it gets assigned to its own value which is that of the previous iteration, so it's int(4).
Unsetting $value in between the two loops resolves the situation. See also an earlier answer by me.

Why does foreach increase refcount by 2 instead of 1?

NikiC stated in another thread:
Right before [a foreach] iteration the $array is "soft copied" for
use in foreach. This means that no actual copy is done, but only the
refcount of the zval of $array is increased to 2.
However, my test code is showing a different result:
$array = array(0, 1, 2);
xdebug_debug_zval('array'); // refcount=1, is_ref=0
// so far so good
foreach ($array as $key => $value) {
xdebug_debug_zval('array'); // refcount=3, is_ref=0
} // why is refcount 3 instead of 2?
Just by looking at the code, we can see at most two array variables.
Why is refcount 3?
Why isn't refcount 2 after foreach is run?
The xdebug_debug_zval() is looking at the $array variable and not the $key variable.
if you change your code to:
foreach ($array as $key => $value) {
echo $key . " : " . $values . "<br>";
//xdebug_debug_zval('array');
}
The correct values of the array will be returned. I don't have the xdebug function so I can't test what value you put in there.

Grab ordinal number of the array value inside foreach

Hello,
I want to grab the ordinal number of an array key inside a foreach loop.
Example
<?php
$array = array("id" => 2, "username" => "foobar");
foreach($array as $value){
$array_key = search_array($value, $array);
//find the ordinal number of $array_key from $array here.
echo $value;
}
count returns the entire number of array keys in the array, i need to grab the ordinal number of the array key.
I hope you guys understand what i ask.
From what I understand, if an entry has a string key, it doesn't have an ordinal position in the array. From http://php.net/manual/en/language.types.array.php:
An array in PHP is actually an ordered map. A map is a type that associates values to keys. This type is optimized for several different uses; it can be treated as an array, list (vector), hash table (an implementation of a map), dictionary, collection, stack, queue, and probably more. As array values can be other arrays, trees and multidimensional arrays are also possible.
Ordered maps don't assign ordinal keys on top of the already existing string keys.
What you could do though, to get a psuedo-ordinal-key is increment a variable.
$i=0;
foreach( $array as $key => $value ) {
echo $i.':'.$key.':'.$value;
$i++;
}
Will echo out each ordinal key, key, and value in the array.
Have another variable that will increase in value after each iteration.
You can use the current function like so:
foreach($array as $value){
$array_key = search_array($value, $array);
echo current( $array );
echo $value;
}
Or you can just add a counter to your loop like so:
$count = 0;
foreach($array as $value){
$array_key = search_array($value, $array);
echo $count++;
echo $value;
}
<?php
$arry = array("id" => 2; "username" => "foobar");
$idx = 0;
foreach($array as $value){
if(array_search(arry, $value)) echo "element found: ".$idx;
$idx++;
}
?>
What you need is another variable - $idx in the above example to count the iterations, as you can't use the "key" corresponding to the value as they're named.
Also the search function is called array_search indeed.

Categories