array_map on collection with array interfaces?

懵懂的女人 提交于 2019-12-19 05:02:59

问题


I have a class called Collection which stores objects of same type. Collection implements array interfaces: Iterator, ArrayAccess, SeekableIterator, and Countable.

I'd like to pass a Collection object as the array argument to the array_map function. But this fails with the error

PHP Warning: array_map(): Argument #2 should be an array

Can I achieve this by implementing other/more interfaces, so that Collection objects are seen as arrays?


回答1:


The array_map() function doesn't support a Traversable as its array argument, so you would have to perform a conversion step:

array_map($fn, iterator_to_array($myCollection));

Besides iterating over the collection twice, it also yield an array that will not be used afterwards.

Another way is to write your own map function:

function map(callable $fn)
{
    $result = array();

    foreach ($this as $item) {
        $result[] = $fn($item);
    }

    return $result;
}

Update

Judging by your use-case it seems that you're not even interested in the result of the map operation; therefore it makes more sense to use iterator_apply().

iterator_apply($myCollection, function($obj) {
    $obj->method1();
    $obj->method2();

    return true;
});



回答2:


array_map wants, as the name suggests, arrays. It's not called iterator_map after all. ;)

Apart from iterator_to_array(), which produces a potentially large temporary array, there's no trick to make iterable objects work with array_map.

The Functional PHP library has a map implementation which works on any iterable collection.




回答3:


I came up with the following solution:

//lets say you have this iterator
$iterator = new ArrayIterator(array(1, 2, 3));

//and want to append the callback output to the following variable
$out = [];

//use iterator to apply the callback to every element of the iterator
iterator_apply(
    $iterator,
    function($iterator, &$out) {
        $current = $iterator->current();
        $out[] = $current*2;
        return true;
    },
    array($iterator, &$out) //arguments for the callback
);

print_r($out);

This way, you can generate an array without iterating twice as you would to with the approach like:

$iterator = new ArrayIterator(array(1,2,3));
$array = iterator_to_array($iterator); //first iteration
$output = array_map(function() {}, $array); //second iteration

Good luck!




回答4:


If you're not interested in creating a new array that is a function mapped over the original array, you could just use a foreach loop (because you implement Iterator).

foreach($item in $myCollection) {
    $item->method1();
    $item->method2();
}

if you actually want to use map, then I think you'll have to implement your own. I would suggest making it a method on Collection, eg:

$mutatedCollection = $myCollection->map(function($item) { 
    /* do some stuff to $item */
    return $item;
});

I would ask yourself if you really want to use map or do you really just mean foreach




回答5:


I just stumbled upon this question and I managed to cast the collection to an array to make it work:

array_map($cb, (array) $collection);

disclaimer For the original question this might not be a suitable option but I found the question while looking to solve a problem which I solved with this solution. I would recommend using a custom iterator map where possible/viable.

another option is to do something like this:

foreach($collection as &$item) {
    $item = $cb($item);
}

which will mutate the underlying collection.

EDIT:

It has been pointed out that casting to an array can have unwanted side effects. It would be better to add a method to your collection to return the array from the iterator, and traverse that, or otherwise add a map method which accepts a callback and run a loop on the underlying iterator.



来源:https://stackoverflow.com/questions/16273233/array-map-on-collection-with-array-interfaces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!