PHP foreach() is amending the array - php

The following code which is displaying a value from within an array is presenting different results when wrapped in a foreach() as opposed to when I use a simple for-loop. The for-loop is presenting the correct data but the foreach() appears to be amending the input array with every iteration.
$arr = array_merge($arr1, $arr2);
for ($x = 0; $x < count($arr); $x++) {
echo $arr[90]['circread_value'];
}
foreach ($arr as $unused) {
echo $arr[90]['circread_value'];
}
The output from the for-loop is the same value over and over again (as expected):
1382429.00
1382429.00
1382429.00
1382429.00
...
The output from the foreach() shows that the 91st element in the array is changing with each iteration:
56256.00
45652.00
50726.00
317896.00
...
How can this be?
Note: I know the code above is contrived (obviously within the foreach() I'm actually wanting to do further processing and refer to each element of the array that I'm iterating through, not just look at the 91st element.) I have simply pared back the code to something simple (as part of my debugging, as much as for posting here!)
Some further information:
$arr has been created by array_merge'ing two 91-element arrays to create a 182 element array.
The behaviour I see is only happening for the 91st element - if I echo out $arr[89]['circread_value'] or $arr[91]['circread_value'], I get consistent values from the foreach() too.
The (seemingly random) values that I see in the foreach() are actually values from other elements in the array (the array as it looks prior to beginning the foreach)
The input arrays ($arr1 and $arr2) can be found here: http://pastebin.com/wQN8XXu2
Thanks for any insight. Don't hesitate to ask for further information.

foreach modifies the internal array pointer, for doesn't, because it expects you to supply an integer offset, as evidenced here http://us3.php.net/manual/en/control-structures.foreach.php
"As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior."

There is something wrong with your code brother.
I've just tested this:
$arr = array(
0 => array('blah' => 123.42),
1 => array('blah' => 5488.87),
90 => array('blah' => 669874.923)
);
for ($x = 0; $x < count($arr); $x++) {
echo $arr[90]['blah'] . PHP_EOL;
}
foreach ($arr as $unused) {
echo $arr[90]['blah'] . PHP_EOL;
}
And it outputs fine:
669874.923
669874.923
669874.923
669874.923
669874.923
669874.923
Do you mind showing us a little more of your code?

Problem found. That 91st element of the array was actually a reference to the array. The foreach was then using the same variable name (reference to the same array - named $unused in the example snippet I gave in the question) so both were looking at the same array.
We noticed the "&" in the array dump after posting it on here for you guys, so StackOverflow has helped our debug process... thanks for everyone's input.

Related

Laravel make array of another array objects

I have general data array and I need to get array of specific data inside this general array so I can match it against my database.
Code
$nums = [];
foreach($request->phones as $phone) {
foreach($phone['_objectInstance']['phoneNumbers'] as $number) {
$nums = $number['value'];
}
}
$contacts = User::whereIn('phone', $nums)->get();
PS: $number['value'] is the data that I want to make array of it.
Sample data that I receive in backend
current error
Argument 1 passed to Illuminate\\Database\\Query\\Builder::cleanBindings() must be of the type array, string given, called in /home/....../vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php on line 918
exception: "TypeError"
Question
How can I make array of my numbers?
Ps: please if you know cleaner way to write this code, than my code above don't hesitate to share with me.
You're assigning $nums to be a new string on every iteration of the loop, rather than appending it to the array.
Just switch this line out:
$nums = $number['value'];
For
$nums[] = $number['value'];
Here are the docs for array_push(), which is the long way of writing the second line.
You are declaring $nums array, but inside the loop, you re-declaring it by a string again.
Fix the array assignments like that.
$nums[] = $number['value'];

Unexpected behavior with references

I have some unexpected behavior with references:
foreach ($this->data as &$row)
{
$row['h'] = 1;
}
foreach ($this->data as $id => $row)
{
... in some cases $row[$id] = $row;
}
The result is that the last element of the array is replaced with the second to last element of the array. It is fixed with the following code:
foreach ($this->data as $key => $row)
{
$this->data[$key]['h'] = 1;
}
Unfortunately, I don't have more time to spend on this. Maybe it is an error with PHP (PHP 5.5.9-1ubuntu4) or something I don't know about references?
There is a perfectly logical explanation and this is not a bug!
PHP 5 introduces the possibility of modifying the contents of the array directly by assigning the value of each element to the iterated variable by reference rather than by value. Consider this code, for example:
$a = array (’zero’,’one’,’two’);
foreach ($a as &$v) {
}
foreach ($a as $v) {
}
print_r ($a);
It would be natural to think that, since this little script does nothing to the array, it will not affect its contents... but that’s not the case! In fact, the script provides the following output:
Array
(
[0] => zero
[1] => one
[2] => one
)
As you can see, the array has been changed, and the last key now contains the value ’one’. How is that possible? The first foreach loop does not make any change to the array, just as we would expect. However, it does cause $v to be assigned a reference to each of $a’s elements, so that, by the time the loop is over, $v is, in fact, a reference to $a[2].
As soon as the second loop starts, $v is now assigned the value of each element. However, $v is already a reference to $a[2]; therefore, any value assigned to it will be copied automatically into the last element of the arrays! Thus, during the first iteration, $a[2] will become zero, then one, and then one again, being effectively copied on to itself. To solve this problem, you should always unset the variables you use in your by-reference foreach loops—or, better yet, avoid using the former altogether.
When looping over an array by reference, you need to manually let go of the reference at the end of your for loop to avoid weird behaviors like this one. So your first foreach should be:
foreach ($this->data as &$row)
{
.... code ....
}
unset($row);
In this case, unset is only destroying the reference, not the contents referenced by $row.
See the warning in the PHP foreach documentation

Strange foreach loop after modifying array

As I wrote some code, PHP confused me a little as I didn't expected the result of the following code:
$data = array(array('test' => 'one'), array('test' => 'two'));
foreach($data as &$entry) {
$entry['test'] .= '+';
}
foreach($data as $entry) {
echo $entry['test']."\n";
}
I think it should output
one+
two+
However the result is: http://ideone.com/e5tCsi
one+
one+
Can anyone explain to me why?
This is expected behaviour, see also https://bugs.php.net/bug.php?id=29992.
The reference is maintained when using the second foreach, so when using the second foreach the value of $entry, which points still to $data[1], is overwritten with the first value.
P.s. (thanks to #billyonecan for saying it): you need to unset($entry) first, so that your reference is destroyed.
This is mentioned specifically in the documentation for foreach. You should unset the loop variable when it gets elements of the array by reference.
Warning
Reference of a $value and the last array element remain even after the
foreach loop. It is recommended to destroy it by unset().

PHP: replace array value doesn't stay after foreach loop

I'm changing the value in a multi-dimensional array and it's not staying outside of the foreach loop that's being used to traverse it.
My array initially looks something like this:
Array
{
[0] => Array
{
[name] => Bob
[age] => 33
[state] => CA
[visited] => 0
}
...
}
My PHP gets into it by going:
foreach ($people as $person){
echo $person['name']
....
logic for the visited variable
...
$person['visited'] = $calculated_visit_value;
}
If I
print_r($person)
at the end (but inside) of the foreach loop everything looks good, the value for visited is set. However, if I print_r($people) outside of the loop, $person['visited'] is not set. I don't know what I'm doing wrong.
Help is appreciated.
You are creating a new variable called $person from within that for loop and your array will never see the scope of that new variable.
You can try passing it by reference, like so:
foreach ($people as &$person){
echo $person['name'];
....
logic for the visited variable
...
$person['visited'] = $calculated_visit_value;
}
From foreach's documentation:
Unless the array is referenced, foreach operates on a copy of the
specified array and not the array itself. foreach has some side
effects on the array pointer. Don't rely on the array pointer during
or after the foreach without resetting it.
What this means is that your $person variable is a copy of what was in the array, similar in effect to this code (note that this code is for understanding only and wrong on many levels, in reality you would use the reset(), current() and next() function to loop properly over your array, see here):
for ($i = 0; $i < count($people); $i++) {
$person = $people[$i];
// code inside your foreach ...
}
So if you change the content of $person, you don't actually modify what's inside the $people array
To solve that, you can either use a referenced foreach:
foreach ($people as &$person) { // note the &
$person = $calculated_visit_value; // $person is now a reference to the original value inside $people and thus this will work as intended
}
Note that the refence is not cleared when the foreach loop ends, so at the end of it $person is still a reference to the last element of $people.
If you don't know what references are, please refer to the documentation for more info.
Or use the key to access the original array:
foreach ($people as $person_index => $person) {
$people[$person_index] = $calculated_visit_value;
}
For your information, you can use the two together
foreach ($people as $person_index => &$person { ...
The $person array is generated on each iteration, so setting that value would be overwritten on the next go through anyway.
But even so, that array only exists during the loop. You should create another array before the loop and put your values into that array during the loop.
As it has been told, "you are creating a new variable called $person from within that for loop and your array will never see the scope of that new variable."
I find this solution more robust :
foreach ($people as $key => $person)
{
echo $person['name'];
//logic for the visited variable
$people[$key]['visited']=$calculated_visit_value;
}

php get 1st value of an array (associative or not)

This might sounds like a silly question. How do I get the 1st value of an array without knowing in advance if the array is associative or not?
In order to get the 1st element of an array I thought to do this:
function Get1stArrayValue($arr) { return current($arr); }
is it ok?
Could it create issues if array internal pointer was moved before function call?
Is there a better/smarter/fatser way to do it?
Thanks!
A better idea may be to use reset which "rewinds array's internal pointer to the first element and returns the value of the first array element"
Example:
function Get1stArrayValue($arr) { return reset($arr); }
As #therefromhere pointed out in the comment below, this solution is not ideal as it changes the state of the internal pointer. However, I don't think it is much of an issue as other functions such as array_pop also reset it.
The main concern that it couldn't be used when iterating over an array isn't an problem as foreach operates on a copy of the array. The PHP manual states:
Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself.
This can be shown using some simple test code:
$arr = array("a", "b", "c", "d");
foreach ( $arr as $val ){
echo reset($arr) . " - " . $val . "\n";
}
Result:
a - a
a - b
a - c
a - d
To get the first element for any array, you need to reset the pointer first.
http://ca3.php.net/reset
function Get1stArrayValue($arr) {
return reset($arr);
}
If you don't mind losing the first element from the array, you can also use
array_shift() - shifts the first value of the array off and returns it, shortening the array by one element and moving everything down. All numerical array keys will be modified to start counting from zero while literal keys won't be touched.
Or you could wrap the array into an ArrayIterator and use seek:
$array = array("foo" => "apple", "banana", "cherry", "damson", "elderberry");
$iterator = new ArrayIterator($array);
$iterator->seek(0);
echo $iterator->current(); // apple
If this is not an option either, use one of the other suggestions.

Categories