Подтвердить что ты не робот

Array_map для коллекции с интерфейсами массива?

У меня есть класс под названием Collection, который хранит объекты того же типа. Collection реализует интерфейсы массива: Iterator, ArrayAccess, SeekableIterator и Countable.

Я хотел бы передать объект Collection в качестве аргумента массива функции array_map. Но это не с ошибкой

Предупреждение PHP: array_map(): Аргумент # 2 должен быть массивом

Могу ли я достичь этого, реализуя другие/более интерфейсы, чтобы объекты Collection рассматривались как массивы?

4b9b3361

Ответ 1

Функция array_map() не поддерживает Traversable в качестве аргумента массива, поэтому вам нужно будет выполнить шаг преобразования:

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

Помимо повторения итерации по коллекции дважды, он также дает массив, который впоследствии не будет использоваться.

Другой способ - написать собственную функцию карты:

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

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

    return $result;
}

Обновление

Судя по вашему делу, кажется, что вас даже не интересует результат операции с картой; поэтому имеет смысл использовать iterator_apply().

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

    return true;
});

Ответ 2

array_map хочет, как следует из названия, массивов. В конце концов, он не называется iterator_map.;)

Помимо iterator_to_array(), который создает потенциально большой временный массив, нет никакого трюка для создания итеративных объектов с array_map.

Функциональная библиотека PHP имеет реализацию map, которая работает в любой итеративной коллекции.

Ответ 3

Я придумал следующее решение:

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

Таким образом, вы можете сгенерировать массив без итерации дважды, как вам хотелось бы с помощью подхода, например:

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

Удачи!

Ответ 4

Если вы не заинтересованы в создании нового массива, который является функцией, сопоставленной с исходным массивом, вы можете просто использовать цикл foreach (потому что вы реализуете Iterator).

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

если вы действительно хотите использовать карту, тогда я думаю, вам придется реализовать свои собственные. Я бы предложил сделать его методом в коллекции, например:

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

Я бы спросил, действительно ли вы хотите использовать map, или просто вы имеете в виду foreach

Ответ 5

Я просто наткнулся на этот вопрос, и мне удалось преобразовать коллекцию в массив, чтобы она заработала:

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

Отказ от ответственности Для первоначального вопроса это не может быть подходящим вариантом, но я нашел вопрос, пытаясь решить проблему, которую я решил с помощью этого решения. Я рекомендовал бы использовать пользовательскую карту итератора, где это возможно/жизнеспособно.

Другой вариант - сделать что-то вроде этого:

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

который будет мутировать основную коллекцию.

РЕДАКТИРОВАТЬ:

Было отмечено, что приведение к массиву может иметь нежелательные побочные эффекты. Было бы лучше добавить метод в вашу коллекцию, чтобы он возвращал массив из итератора, и перебрать его, либо добавить метод map который принимает обратный вызов и запускает цикл на базовом итераторе.