5

when using iterators in PHP you can use iterator_to_array function to kind of extract the array resulting of iterating. For example, let's say you have following ArrayObject:

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

As you see, its storage is a bi-dimensional array.

We can crete a FilterOperator to only accept its first item (I know it would be better with LimitIterator, it's just as an example purpose):

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

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

Now, if i do:

print_r(iterator_to_array($filter_iterator));

I get the array I could get if I manually loop through the operator:

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

But what now if I want to work with a RecursiveFilterIterator? Let's say I have:

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

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

As you see, this will accept only key 0 for each array contained in the parent array. And so it works if I recursive iterate over it:

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

Results in:

1
5
9

But, how could I get quickly the array 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()));

I get whole original array:

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)));

I get just first item:

Array ( [0] => 9 )

If I do:

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

I get last item in my parent array but with key 0:

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

What I need is to get the array:

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

I know I can get it manually looping, but I want to know if there is a direct way, like in iterator_to_array for not recursive iterators. Sure there is something I don't understand about recursive iterators in PHP, but its documentation is really bad in this.

Thank you very much.

1
  • "its documentation is really bad" — any feedback on this topic is welcomed, send me an email (my username on SO, @php.net). Commented Feb 4, 2012 at 13:56

3 Answers 3

3

It is not entirely clear what you are really wanting to do, but the following takes a RecursiveArrayIterator (note: ArrayObject is not a recursive iterator) and uses iterator_to_array() to get the resulting array that you want.

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));
Sign up to request clarification or add additional context in comments.

1 Comment

Ok, thank you @salathe. This really works. But I think it's quite strange, or at least I don't understand it too much. PHP documentation says that ParentIterator is FilterIterator extended to show only elements which have children. So I think its accept() method only determine if its current element has children. Having to overwrite current() method to filter and not accept() I think its not very clean. Furthermore, my example should work because I'm checking as well if current has a children, but it doesn't.
0

Do you need/want to use iterators? You could use php's built-in array_map() function which takes an array and a function and applies the specified function to each element in the array. So you could do:

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

The result will be:

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

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

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

I apologise if I have misunderstood your question.

1 Comment

Yes, I would like to achieve this but using iterators :) Thank you anyway, @csOlar
0

OK. One easy way could be the following. It extends the ArrayIterator class and overrides the current() function to return the sliced array:

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

1 Comment

Hey, yes, I know there is easy ways to achieve what I want, but what I'd like is to get that using recursive iterators. Thank you anyway.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.