I need to loop through an array twice - once to modify its values, and once to display its content in html. Unfortunately, I am running into trouble. I have created a test scenario to illustrate the issue.
$cases = array(
array('caseStyle' => 'case style 1', 'caseNum' => 'case01'),
array('caseStyle' => 'case style 2', 'caseNum' => 'case02'),
array('caseStyle' => 'case style 3', 'caseNum' => 'case03'),
array('caseStyle' => 'case style 4', 'caseNum' => 'case04'),
array('caseStyle' => 'case style 5', 'caseNum' => 'case05'),
array('caseStyle' => 'case style 6', 'caseNum' => 'case06')
);
foreach ($cases as $k => &$v) {
$v['caseNum'] = ucwords($v['caseNum']);
}
foreach ($cases as $k => $v) {
echo $v['caseNum'] . ' - ' . $v['caseStyle'] . '<br/>';
}
This outputs:
Case01 - case style 1
Case02 - case style 2
Case03 - case style 3
Case04 - case style 4
Case05 - case style 5
Case05 - case style 5
Note the values for the last item are wrong.
If during the second iteration I use
foreach($cases as $k => $d)
instead of foreach($cases as $k => $v)
or if I unset $v
before the second iteration (unset($v))
everything goes fine. This is intriguing. What am I missing?
When you execute a foreach
loop, the variables in the ()
persist after the loop is done. That means that after the first loop finishes, you can var_dump($v)
and get the values that were contained in it after the last iteration through the first loop. This is the case whether it's a reference (&$v
) or a normal variable ($v
).
However, if it's a reference in the first loop, it remains a reference unless it's unset. That means when you enter the second loop, you're overwriting the reference with the value of the array element you're currently looking at.
Remember, what foreach($cases as $k => $v)
really means is "take the key for this element in $cases and assign that to $k, and take the value for this element and assign it to $v)". Since $v is still a reference to the last element in the array, rather than setting the value of a new variable $v, you're actually updating the value of where $v already points to.
What that means is, if we simplify $cases to be simply ['a', 'b', 'c', 'd']
, after the first time through the second foreach, $cases is now ['a', 'b', 'c', 'a']
because you've reassigned the element in $cases that $v points to - the last one - to have the same value as the first one. The second time through, it's ['a', 'b', 'c', 'b']
. The third time through it's ['a', 'b', 'c', 'c']
. Then, the last time through, you're assigning it to itself, and at that time it holds the value 'c'
.
This is really just a case of php working as expected. The solution is to unset($v)
as soon as the first loop finishes, to make sure that the next time you use $v you're using a new variable, rather than an existing reference.
To see this in action:
Head on over to http://phpfiddle.org/ and paste the following code, and run it; you'll see in the output that $v
is maintained after the first loop completes, and that the value of $cases[5]
changes each time through the second loop.
$cases = array(
array('caseStyle' => 'case style 1', 'caseNum' => 'case01'),
array('caseStyle' => 'case style 2', 'caseNum' => 'case02'),
array('caseStyle' => 'case style 3', 'caseNum' => 'case03'),
array('caseStyle' => 'case style 4', 'caseNum' => 'case04'),
array('caseStyle' => 'case style 5', 'caseNum' => 'case05'),
array('caseStyle' => 'case style 6', 'caseNum' => 'case06')
);
foreach ($cases as $k => &$v) {
$v['caseNum'] = ucwords($v['caseNum']);
}
var_dump($v);
echo "<br />";
foreach ($cases as $k => $v) {
print_r($cases);
echo "<br />";
echo $k . ': ' . $v['caseNum'] . ' - ' . $v['caseStyle'] . '<br/>';
}
Try this:
foreach ($cases as $k => &$v) {
$cases[$k]['caseNum'] = ucwords($v['caseNum']);
echo $cases[$k]['caseNum'] . ' - ' . $cases[$k]['caseStyle'] . '<br/>';
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With