I'm currently working on a generic form creation class and had an issue yesterday. I made a snippet to reproduce the problem.
Essentially I want to delete elements that are grouped from the original elements array after the whole group has been drawn and I'm doing this while looping over the elements array.
The code snippet should cover the problem, am I missing something here? From my knowledge deleting an element while foreach is completely safe and legal since foreach internally only uses a copy that may be modified during the loop.
$ids = array('a' => array(), 'b' => array(), 'c' => array());
$groups['g1'] = array('a', 'c');
foreach($ids as $id => $element) {
//var_dump($ids);
$g_id = '';
// search the id in all groups
foreach($groups as $group_id => $group) {
if(in_array($id, $group)) {
$g_id = $group_id;
break;
}
}
// element is part of a group
if($g_id !== '') {
//echo $g_id;
// element a and c gets unset within loop and should not be in $ids anymore
foreach($groups[$g_id] as $field_id) {
unset($ids[$field_id]);
echo $field_id;
}
unset($groups[$g_id]);
} else {
if($id === 'a' || $id === 'c')
echo $id;
}
}
Element 'c' gets unset within the foreach(groups ..) loop but is afterwards again outputted in the else branch. Also when i var_dump($fields) at the beginning i always get 'a', 'b' and 'c' inside. I'm using PHP 5.4.7.
Thanks in advance
EDIT: i made a mistake in the sample code, its now updated. All comments about using the wrong index (it would have been 0,1 etc) were correct of course. The values when using var_dump are unset now, but i still get into the else with 'c' one time.
EDIT2: Im not done with the original code but after reading through the comments I currently came up with following solution to the posted code snippet above:
$ids=array("a"=>array(),"b"=>array(),"c"=>array(),"d"=>array(),"e"=>array());
$groups=array(array("a"),array("c", "e"));
array_walk($groups,function($v,$i)use(&$ids){
$in_both = array_intersect(array_keys($ids),$v);
//var_dump($in_both);
foreach($in_both as $b) {
unset($ids[$b]);
}
});
print_r($ids);
or
$ids=array("a"=>array(),"b"=>array(),"c"=>array(),"d"=>array(),"e"=>array());
$groups=array(array("a"),array("c"));
array_walk($ids,function($v,$i)use(&$ids, $groups){
$in_both = array();
foreach($groups as $g) {
if(in_array($i,$g)) {
$in_both = array_intersect(array_keys($ids),$g);
}
}
foreach($in_both as $b) {
unset($ids[$b]);
}
});
print_r($ids);
Using a foreach does not work for me in this case, because i need to change the $ids array while the loop is iterating over it.
In the very most basic situation a code something like this:
$ids = array('a', 'b');
while(count($ids)) {
array_pop($ids);
echo 'pop';
}
echo 'empty';
Allthough foreach can change the original values from the array it will not change the copy of the array used for the iteration as nl-x already stated. Thanks to Passerby for the idea of using array_walk for this.
EDIT3: Updated code snipped once more. The second snipped allthough behaves undefined as well. Deleting elements from an array while iterating over its seems to be a bad idea.