In the following SSCCE, why isn't the for
loop being executed for $a
greater than 3, although the condition should let it execute till $a
becomes 5.
And the output of the last statement is even more weird.
What I am trying to achieve is that I want to delete the elements have the Select one
value for the element/variable word
, such that the resulting array is:
Array ( [0] => stdClass Object ( [word] => alpha [sentence] => A is the first letter in the word Alpha. ) [1] => stdClass Object ( [word] => beta [sentence] => B is the first letter in the word Beta. ) )
The question is that what is messing it up and what can I do to fix it?
<?php
$objectOne = new stdClass;
$objectOne->word = 'alpha';
$objectOne->sentence = 'A is the first letter in the word Alpha.';
$objectTwo = new stdClass;
$objectTwo->word = 'beta';
$objectTwo->sentence = 'B is the first letter in the word Beta.';
$objectThree = new stdClass;
$objectThree->word = 'Select one';
$objectThree->sentence = '';
$items = array($objectOne, $objectTwo, $objectThree, $objectThree, $objectThree, $objectThree );
print_r($items);//check
echo '<br><br>count($items) >> '.count($items).'<br><br>';//check
for ($a=0; $a < count($items); $a++) {
echo '<br><br>We are entering index '.$a.'<br><br>';//check
echo '<br>'.$items[$a]->word.'<br>';//check
if ( ($items[$a]->word)=="Select one" ) {
echo '<br>YES if ( ($items['.$a.']->word)=="Select one" ) AT '.$a.' INDEX.<br>';//check
unset($items[$a]);
/**/array_splice($items, $a, 1);
}
echo '<br><br>We are leaving index '.$a.'<br><br>';//check
}
echo '<br><br>AFTER:-<br>';//check
print_r($items);//check
?>
OUTPUT:
Array ( [0] => stdClass Object ( [word] => alpha [sentence] => A is the first letter in the word Alpha. ) [1] => stdClass Object ( [word] => beta [sentence] => B is the first letter in the word Beta. ) [2] => stdClass Object ( [word] => Select one [sentence] => ) [3] => stdClass Object ( [word] => Select one [sentence] => ) [4] => stdClass Object ( [word] => Select one [sentence] => ) [5] => stdClass Object ( [word] => Select one [sentence] => ) )
count($items) >> 6
We are entering index 0
alpha
We are leaving index 0
We are entering index 1
beta
We are leaving index 1
We are entering index 2
Select one
YES if ( ($items[2]->word)=="Select one" ) AT 2 INDEX.
We are leaving index 2
We are entering index 3
Select one
YES if ( ($items[3]->word)=="Select one" ) AT 3 INDEX.
We are leaving index 3
AFTER:-
Array ( [0] => stdClass Object ( [word] => alpha [sentence] => A is the first letter in the word Alpha. ) [1] => stdClass Object ( [word] => beta [sentence] => B is the first letter in the word Beta. ) [2] => stdClass Object ( [word] => Select one [sentence] => ) )
The condition is not always true. The condition in your for loop recalculates the size of the array in each iteration. The length of the array changes whenever an item is removed.
The value of $a
and count($items)
each time the condition is checked is as follows:
$a | count($items) | $a < count($items)
---------------------------------------
0 | 6 | true
1 | 6 | true
2 | 6 | true
3 | 5 | true -- $items[2] was removed
4 | 4 | false -- $items[3] was removed
You should store the size of the array in a variable and use that instead. Also since array_splice
doesn't preserve numeric keys you will eventually get an undefined offset notice when trying to access $items[4]
and $items[5]
. That line is not required.
$count = count($items);
for ($a=0; $a < $count; $a++) {
Better yet you can use a foreach
instead of the for
and use $item
instead of $items[$a]
:
foreach ($items as $a=>$item) {
echo '<br><br>We are entering index '.$a.'<br><br>';//check
echo '<br>'.$item->word.'<br>';//check
...
unset($items[$a]); //can't use $item because it is a copy and not a reference