我目前正在开发一个通用表单创建类,昨天遇到了问题。 我制作了一个片段来重现这个问题。
基本上我想删除整个组绘制后从原始元素数组中分组的元素,并且在循环遍历元素数组时这样做。
代码片段应该涵盖问题,我在这里遗漏了什么吗?根据我的知识删除元素,而foreach是完全安全和合法的,因为foreach内部只使用可能在循环期间修改的副本。
$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;
}
}
元素'c'在foreach(groups ..)循环中未设置,但之后再次在else分支中输出。另外当我在开头的var_dump($ fields)时,我总是在里面得到'a','b'和'c'。我正在使用PHP 5.4.7。
提前致谢
编辑:我在示例代码中犯了一个错误,现在已经更新了。关于使用错误索引(它本来是0,1等)的所有评论都是正确的。 使用var_dump时的值现在未设置,但我仍然使用'c'一次进入else。
EDIT2: 我没有完成原始代码,但在阅读完评论之后,我目前想出了以下解决方案到上面发布的代码片段:
$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);
或
$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);
在这种情况下,使用foreach对我来说不起作用,因为我需要在循环迭代时更改$ ids数组。
在最基本的情况下,代码是这样的:
$ids = array('a', 'b');
while(count($ids)) {
array_pop($ids);
echo 'pop';
}
echo 'empty';
尽管foreach可以更改数组中的原始值,但它不会更改用于迭代的数组副本,因为nl-x已经说明了。 感谢Passerby为此使用array_walk的想法。
EDIT3: 更新的代码再次被剪切。第二个剪断的行为也是不确定的。在迭代数组时从数组中删除元素似乎是一个坏主意。
答案 0 :(得分:1)
$ids[$field_id]
不存在,您使用的是值而非键。
您应该使用正确的密钥取消设置:
if (in_array($field_id, $ids))
unset($ids[array_search($field_id, $ids)]);
答案 1 :(得分:1)
我现在正在仔细检查。但我认为使用foreach取消数组是而不是非常安全。
我通常会做的是采取foreach,并从最高的索引开始并沿途减少索引。 for($i = count($arr)-1; $i >= 0; $i--) { unset($array[$i]); }
我会在几分钟内编辑这篇文章。
编辑我很困惑。 $ i ++的for确实是罪魁祸首。 foreach是安全的(在php中!不是所有语言)
<?php
$arr = Array(1,2,3,4,5,6,7,8,9,10);
foreach ($arr as $key=>$val)
unset($arr[$key]);
echo implode(',',$arr); // returns nothing
$arr = Array(1,2,3,4,5,6,7,8,9,10);
for ($i=0; $i<count($arr); $i++)
unset($arr[$i]);
echo implode(',',$arr); // returns 6,7,8,9,10
$arr = Array(1,2,3,4,5,6,7,8,9,10);
for ($i=count($arr)-1; $i>=0; $i--)
unset($arr[$i]);
echo implode(',',$arr); // returns nothing
?>
答案 2 :(得分:1)
花了一些时间阅读您的代码,我猜测您的程序是:
$ids
中的每个元素,检查它是否存在于$groups
; $ids
中的所有内容。按照上述逻辑,我想出了这个:
$ids=array("a","b","c","d","e");
$groups=array(array("a","c"),array("c","e"));
array_walk($groups,function($v,$i)use(&$ids){
$ids=array_diff($ids,$v);
});
print_r($ids);//debug
答案 3 :(得分:1)
Chris,如果我理解正确,你不希望在其他分支中输出'C'吗?
但它应该输出。你的逻辑是:
foreach($ids as $id)
没有任何影响,因为即使在清除了ids数组后,foreach将继续使用未触及的副本。)所以总输出确实是acc。
很高兴知道foreach()即使在其元素被清除之后仍继续使用未经修改的副本,这是一个特定的PHP事物。其他语言也不一定相同。
答案 4 :(得分:0)
如果你想从数组中删除元素,那么你不应该用array_splice
“拼接”它吗?
答案 5 :(得分:0)
由于使用了数组副本,您的foreach不会进行任何更改..您需要使用pass by reference才能使其正常工作。其中一种方法如下所述
while(list($key,$value) = each($array)){
if(your reason to unset)
unset($array[$key]);
}
这将从数组中删除元素。