вид 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() Мисля, че не е много чист. Освен това моят пример трябва да работи, защото проверявам дали current има деца, но не. - person Waiting for Dev...; 09.02.2012

Имате ли нужда/искате ли да използвате итератори? Можете да използвате вградената функция array_map() на php, която взема масив и функция и прилага определената функция към всеки елемент в масива. Така че можете да направите:

<?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