2010-02-03 65 views
2

我有一个数组结构类似:如何过滤一个数组,以删除父母等于零的孩子?

$something = array(
    0 => array(
     'label' => 'Foo', 
     'items' => array(
      '123' => 4, 
      '124' => 0, 
     ) 
    ), 
    1 => array(
     'label' => 'Bar', 
     'items' => array(
      '125' => 5, 
      '126' => 1, 
     ) 
    ), 
    2 => array(
     'label' => 'Baz', 
     'items' => array(
      '127' => 0, 
      '128' => 0, 
     ) 
    ) 
); 

,我需要除去所有的“项目”键具有值零,并且如果项目没有孩子的,删除整个块。

所以,过滤该数组后,我应该有:

array(2){ 
    [0]=> 
    array(2) { 
     ["label"]=> "Foo" 
     ["items"]=> 
      array(1) { 
       [123]=> 4 
      } 
    } 
    [1]=> 
    array(2) { 
    ["label"]=> "Bar" 
    ["items"]=> 
     array(2) { 
      [125]=> 5 
      [126]=> 1 
     } 
    } 
} 

我使用array_filter,array_walk和array_walk_recursive(这个作品很好tryed - 但是 - 犯规让我删除回调的关键函数..)没有成功..

我有解构和重新构建一个新的数组,或者我缺少array_ *函数的正确使用吗?

回答

4
$something = array(..); // as defined above 

for ($i = 0, $iMax = count($something); $i < $iMax; $i++) 
{ 
    foreach ($something[$i]['items'] as $key => $value) 
    { 
     if (!$value) 
      unset($something[$i]['items'][$key]); 
    } 

    if (count($something[$i]['items']) == 0) 
     unset($something[$i]); 
} 
$something = array_values($something); // reset indices 
1

我看不到的方式与array_walk_recursive做到这一点,所以只会像这样的东西去:

/** 
* Removes values from an array if the callback function is true. 
* Removes empty child arrays 
*/ 
function array_remove_recursive(array $haystack, $f){ 
    if (empty($haystack)){ 
     return $haystack; 
    } 
    foreach ($haystack as $key => $val){ 
     if (is_array($val){ 
      $haystack[$key] = array_remove_recursive($val); 
      if (empty($haystack[$key]){ 
       unset($haystack[$key]); 
      } 
     }elseif ($f($val) === true){ 
      unset($haystack[$key]); 
     } 
    } 
    return $haystack; 
} 

基于“每个功能做一两件事,一件事唯一”的原则,这可能最好将它分成两个函数,一个是如果函数返回true而另一个去除空的子元素则移除一个元素。这有不得不遍历数组两次的缺点。

如果您传递大量数据,转换为使用引用的函数应该不会太难。

2

好的,这是现在为您的数组定制的。不要指望它与任意阵列结构的工作:

class ItemFilterIterator extends RecursiveFilterIterator 
{ 
    public function accept() 
    { 
     if(is_numeric($this->key()) && is_array($this->current())) { 
      if(array_key_exists('items', $this->current())) { 
       $items = $this->current(); 
       return array_sum($items['items']) > 0; 
      } 
     } elseif(is_numeric($this->key()) && $this->current() === 0) { 
      return false; 
     } 
     return true; 
    } 
} 

当数组在迭代,所有的元素被传递到的ItemFilterIteratoraccept()方法,它会检查当前元素关键是数字。这仅适用于项目中的顶级元素和元素。如果当前元素是一个数组,则检查是否存在具有键项目的元素,并且如果子项的 sum大于零。如果不是,则跳过迭代中的元素。如果它不是数组,但是数字和值为零,则假定我们在项目内,并跳过这些元素。

你使用这样的:

$iterator = new RecursiveIteratorIterator(
       new ItemFilterIterator(
        new RecursiveArrayIterator($something))); 

foreach($iterator as $key => $value) { 
    echo $key, '--', $value, PHP_EOL; // or whatever else you want to do here 
} 

这是一个有趣的练习:)

更多SplIterators:

+0

+1它完美,谢谢!但它对我的实际需要看起来有点矫枉过正..我会用捅的解决方案 – Strae 2010-02-03 11:42:28

相关问题