【问题标题】:Deleting rows in callback function in array_walk()在 array_walk() 中删除回调函数中的行
【发布时间】:2014-12-10 05:56:29
【问题描述】:

我正在尝试使用 array_walk() 函数来处理数组:

<?php

$array = array('n1' => 'b1', 'n2' => 'b2', 'n3' => 'b3');

array_walk($array, function(&$val, $key) use (&$array){
    echo $key."\n";
    if ($key == 'n1')
        $val = 'changed_b1';
    if ($key == 'n2' || $key == 'n3') {
        unset($array[$key]);
    }
});

print_r($array);

获取:

n1
n2
Array
(
    [n1] => changed_b1
    [n3] => b3
)

看来,删除第二个元素后——第三个元素不会被发送到回调函数。

【问题讨论】:

  • 手册中有一个重要说明关于通过数组遍历函数更改结构/取消设置,php.net/manual/en/function.array-walk.php
  • @Ghost,有什么解决问题的建议吗?
  • 在下面查看 hd 的答案,或者为什么不直接使用 foreach
  • 其实这段代码很复杂,如果你只想得到一个没有特定键的数组,使用 unset($array["n2"], $array["n3"]);

标签: php arrays


【解决方案1】:

检查“array_walk”源代码,你会看到。 array_walk 使用 pos 来获取 item,并且在每个循环中,pos 都会向前移动。

    do {
    /* Retrieve value */
    zv = zend_hash_get_current_data_ex(target_hash, &pos);
    

    /* Retrieve key */
    zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);

    /* Move to next element already now -- this mirrors the approach used by foreach
     * and ensures proper behavior with regard to modifications. */
    zend_hash_move_forward_ex(target_hash, &pos);

    /* Back up hash position, as it may change */
    EG(ht_iterators)[ht_iter].pos = pos;

并重置 pos 以获取值

    /* Reload array and position -- both may have changed */
    if (Z_TYPE_P(array) == IS_ARRAY) {
        pos = zend_hash_iterator_pos_ex(ht_iter, array);
        target_hash = Z_ARRVAL_P(array);
    } else if (Z_TYPE_P(array) == IS_OBJECT) {
        target_hash = Z_OBJPROP_P(array);
        pos = zend_hash_iterator_pos(ht_iter, target_hash);
    } else {
        php_error_docref(NULL, E_WARNING, "Iterated value is no longer an array or object");
        result = FAILURE;
        break;
    }  
  • 如果键= n1;然后下一个位置 1
  • 如果键= n2;然后下一个位置 2
  • 如果键= n3;然后下一个 pos 3

当运行 [$key == 'n2'] 时,下一个 pos 是 2 ; unset 后,pos 2 不可达,所以循环结束。

所以实际上,$key=='n3' 不会发生,你会得到结果。

【讨论】:

    【解决方案2】:

    我意识到这是几年前的事了,但我在寻找类似情况的解决方案时发现了这个帖子。

    我最后使用 preg_grep 只返回我想遍历的数组,以防有人发现它有用。

    因此,就我而言,我想忽略 scandir 数组中带有“。”的文件。前缀(系统文件),然后将新前缀应用于其余文件。

    这就是我的结论:

    $fl = array_map(function($i){
        return $new_prefix . "/" . $i;
    }, preg_grep("/^[^\.]/", scandir($path)));
    

    如果您可以构建一个正则表达式来排除不需要的数组元素,那么这个解决方案应该可以工作。

    【讨论】:

      【解决方案3】:

      来自the documentation

      只有数组的值可能会改变;它的结构不能更改,即程序员不能添加、取消设置或重新排序元素。如果回调不遵守此要求,则此函数的行为未定义且不可预测

      这可能是您的代码无法获得所需输出的原因。希望对您有所帮助。

      可能的替代方案:

      1. 如果您仍想使用array_walk(),只需创建一个新数组并将“必需”元素(即您不想删除的索引)复制到新数组中。如果要删除的元素数量非常多,这是首选替代方法。
      2. 您可以查看array_filterarray_map,它们都依赖于将callback 应用于数组的每个元素。您可以简单地在此回调函数中设置一个条件,禁止您要删除的索引。如果您要删除的元素数量很少,这将起作用。
      3. 但是,如果要删除的元素是连续的并形成数组的“部分”(在您的情况下,您想删除相邻的 n2 和 n3)。您可以使用函数array_splice

      旁注 - 我没有输入任何代码 sn-ps,因为我已经链接了相关文档,并且开始使用它们本身应该是一个很好的练习。

      【讨论】:

      • 虽然您添加了一个很好的解释(引用自手册),但也可以使用替代方法来支持它
      • @mochalygin,请查看我的更新答案以了解可能的替代方案。希望它能让你朝着正确的方向开始。
      【解决方案4】:

      使用数组过滤器:

      <?php
      $filtered = array_filter($array, function($v,$k) {
          return $k !== "n2" && $k !== "n3";
      }, ARRAY_FILTER_USE_BOTH);
      ?>
      

      http://php.net/array_filter

      【讨论】:

      • 值得一提的是它适用于 PHP >= 5.6.x
      • @m6w6,需要更改一些元素并取消设置一些元素 -- 所以不能使用array_filter()
      【解决方案5】:

      您可以做的是使用辅助数组,这将产生这些节点已被删除的效果,例如;

      <?php
      
      $array = array('n1' => 'b1', 'n2' => 'b2', 'n3' => 'b3');
      
      $arrFinal = array();
      array_walk($array, function($val, $key) use (&$array, &$arrFinal){
          echo $key."\n";
          if ($key == 'n2' || $key == 'n3') {
              //Don't do anything
          } else {
             $arrFinal[$key] = $val;
          }
      });
      
      print_r($arrFinal);
      

      https://eval.in/206159

      【讨论】:

      • 这似乎是一个拼凑,但如果没有其他建议 - 我会接受它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-15
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多