从foreach中的数组中取消设置元素

时间:2013-04-18 08:05:32

标签: php foreach unset

我目前正在开发一个通用表单创建类,昨天遇到了问题。 我制作了一个片段来重现这个问题。

基本上我想删除整个组绘制后从原始元素数组中分组的元素,并且在循环遍历元素数组时这样做。

代码片段应该涵盖问题,我在这里遗漏了什么吗?根据我的知识删除元素,而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: 更新的代码再次被剪切。第二个剪断的行为也是不确定的。在迭代数组时从数组中删除元素似乎是一个坏主意。

6 个答案:

答案 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

Live demo

答案 3 :(得分:1)

Chris,如果我理解正确,你不希望在其他分支中输出'C'吗?

但它应该输出。你的逻辑是:

  • 你做foreach id并以id'a'开头。
  • 然后你从ids 清除ids a和c并删除包含'a'的组g1 。在此步骤中,将输出已删除的ID,即a和c。 (清除ids中的a和c对foreach($ids as $id)没有任何影响,因为即使在清除了ids数组后,foreach将继续使用未触及的副本。)
  • 然后你做id'b':在任何组中都找不到它。 (实际上,现在还没有留下任何一组)
  • 所以对于'b'你进入了else分支。但是else分支中的if()会阻止输出
  • 然后你做id'c',这在任何一个组中都找不到,因为你有已经删除的组g1 !没有留下任何团体,还记得吗?
  • 所以对于'c'你也进入了else分支。而这次else分支中的if()允许输出!输出只是c

所以总输出确实是acc。

很高兴知道foreach()即使在其元素被清除之后仍继续使用未经修改的副本,这是一个特定的PHP事物。其他语言也不一定相同。

答案 4 :(得分:0)

如果你想从数组中删除元素,那么你不应该用array_splice“拼接”它吗?

从PHP手册:http://php.net/manual/en/function.array-splice.php

答案 5 :(得分:0)

由于使用了数组副本,您的foreach不会进行任何更改..您需要使用pass by reference才能使其正常工作。其中一种方法如下所述

  while(list($key,$value) = each($array)){
   if(your reason to unset)
       unset($array[$key]);
}

这将从数组中删除元素。