тип iterator_to_array для рекурсивного итератора для получения двумерного массива

при использовании итераторов в PHP вы можете использовать функцию iterator_to_array для извлечения массива, полученного в результате итерации. Например, предположим, что у вас есть следующие ArrayObject:

$array_object = new ArrayObject(array(
   array('1', '2', '3', '4'),
   array('5', '6', '7', '8'),
   array('9', '10', '11', '12'),
));

Как видите, его хранилище представляет собой двумерный массив.

Мы можем создать FilterOperator, чтобы принимать только его первый элемент (я знаю, что было бы лучше с LimitIterator, это просто пример):

class myFilterIterator extends FilterIterator
{
   public function accept()
   {
      return ($this->key() === 0);
   }
}

$filter_iterator = new myFilterIterator(new ArrayIterator($array_object));

Теперь, если я сделаю:

print_r(iterator_to_array($filter_iterator));

Я получаю массив, который мог бы получить, если бы вручную перебрал оператор:

Array ( [0] => Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) )

Но что теперь, если я хочу работать с RecursiveFilterIterator? Допустим, у меня есть:

class myRecursiveFilterIterator extends RecursiveFilterIterator
{
   public function accept()
   {
      return ($this->hasChildren() || $this->key() === 0);
   }
}

$recursive_filter_iterator = new myRecursiveFilterIterator(new RecursiveArrayIterator($array_object));

Как видите, это будет принимать только ключ 0 для каждого массива, содержащегося в родительском массиве. И так это работает, если я рекурсивно перебираю его:

foreach (new RecursiveIteratorIterator($recursive_filter_iterator) as $value) {
   print_r($value);
   echo '<br />';
}

Результат:

1
5
9

Но как мне быстро получить массив array(array(1), array(5), array(9)) ?

If I do:

print_r(iterator_to_array($recursive_filter_iterator));

or

print_r(iterator_to_array($recursive_filter_iterator->getInnerIterator()));

or

$it = new RecursiveIteratorIterator($recursive_filter_iterator);
print_r(iterator_to_array($it->getInnerIterator()));

Я получаю весь исходный массив:

Array ( [0] => Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) [1] => Array ( [0] => 5 [1] => 6 [2] => 7 [3] => 8 ) [2] => Array ( [0] => 9 [1] => 10 [2] => 11 [3] => 12 ) )

If I do:

print_r(iterator_to_array(new RecursiveIteratorIterator($recursive_filter_iterator)));

Я получаю только первый элемент:

Array ( [0] => 9 )

If I do:

print_r(iterator_to_array(new RecursiveIteratorIterator($recursive_filter_iterator->getInnerIterator())));

Я получаю последний элемент в своем родительском массиве, но с ключом 0:

Array ( [0] => 9 [1] => 10 [2] => 11 [3] => 12 ) 

Мне нужно получить массив:

Array ( [0] => Array ( [0] => 1 ) [1] => Array ( [0] => 5 ) [2] => Array ( [0] => 9 ) ) 

Я знаю, что могу зациклить вручную, но я хочу знать, есть ли прямой способ, как в iterator_to_array для нерекурсивных итераторов. Конечно, я чего-то не понимаю в рекурсивных итераторах в PHP, но его документация в этом отношении очень плоха.

Большое спасибо.


person Waiting for Dev...    schedule 02.02.2012    source источник
comment
его документация очень плохая — приветствуются любые отзывы по этой теме, пришлите мне электронное письмо (мое имя пользователя на SO, @php.net).   -  person salathe    schedule 04.02.2012


Ответы (3)


Не совсем понятно, что вы действительно хотите сделать, но следующее принимает RecursiveArrayIterator (примечание: ArrayObject не рекурсивный итератор) и использует iterator_to_array() для получения результирующего массива, который вы хотите.

class FirstOnlyRecursiveArrayIterator extends ParentIterator {
    public function __construct(RecursiveArrayIterator $it) {
         parent::__construct($it);
    }
    public function current() {
        $children = parent::current();
        return array_slice($children, 0, 1);
    }
}

$array_it = new RecursiveArrayIterator(array(
    array('1', '2', '3', '4'),
    array('5', '6', '7', '8'),
    array('9', '10', '11', '12'),
));

$filter_iterator = new RecursiveIteratorIterator(
    new FirstOnlyRecursiveArrayIterator($array_it),
    RecursiveIteratorIterator::SELF_FIRST);
print_r(iterator_to_array($filter_iterator));
person salathe    schedule 04.02.2012
comment
Хорошо, спасибо @salathe. Это действительно работает. Но я думаю, что это довольно странно, или, по крайней мере, я не слишком понимаю. Документация PHP говорит, что ParentIterator является FilterIterator расширенным для отображения только тех элементов, у которых есть дочерние элементы. Поэтому я думаю, что его метод accept() определяет только то, есть ли у его текущего элемента дочерние элементы. Приходится перезаписывать метод current() для фильтрации, а не accept(), я думаю, что это не очень чисто. Кроме того, мой пример должен работать, потому что я также проверяю, есть ли у текущего дочерние элементы, но это не так. - person Waiting for Dev...; 09.02.2012

Вам нужно/хотите использовать итераторы? Вы можете использовать встроенную в php функцию array_map(), которая принимает массив и функцию и применяет указанную функцию к каждому элементу массива. Итак, вы можете сделать:

<?php
function get_first($foo)
{ 
    return array_slice($foo, 0, 1); //slice the array right after the first element
}

$array_object = new ArrayObject(array(
     array('1', '2', '3', '4'),
     array('5', '6', '7', '8'),
     array('9', '10', '11', '12'),
));

$new_array = array_map("get_first", $array_object->getArrayCopy());
print_r($new_array);
?>

Результат будет:

Array
(
    [0] => Array
        (
            [0] => 1
        )

    [1] => Array
        (
            [0] => 5
        )

    [2] => Array
        (
            [0] => 9
        )
)

Прошу прощения, если неправильно понял ваш вопрос.

person cs0lar    schedule 02.02.2012
comment
Да, я хотел бы добиться этого, но с помощью итераторов :) В любом случае спасибо, @csOlar - person Waiting for Dev...; 02.02.2012

ХОРОШО. Одним из простых способов может быть следующий. Он расширяет класс ArrayIterator и переопределяет функцию current() для возврата нарезанного массива:

<?php
class FirstOnlyIterator extends ArrayIterator
{
    public function current()
    {
            $next = parent::current();
            return array_slice($next, 0, 1);
    }
}

$array_object = new ArrayObject(array(
     array('1', '2', '3', '4'),
     array('5', '6', '7', '8'),
     array('9', '10', '11', '12'),
));

$iterator = new FirstOnlyIterator($array_object);

print_r(iterator_to_array($iterator));
?>
person cs0lar    schedule 02.02.2012
comment
Эй, да, я знаю, что есть простые способы добиться того, чего я хочу, но я хотел бы получить это с помощью рекурсивных итераторов. Все равно спасибо. - person Waiting for Dev...; 03.02.2012