2013-04-29 13 views
18

Mam klasę o nazwie Collection, która przechowuje obiekty tego samego typu. Collection implementuje interfejsy tablicowe: Iterator, ArrayAccess, SeekableIterator i Countable.array_map w kolekcji z interfejsami tablicowymi?

Chciałbym przekazać obiekt Collection jako argument tablicowy do funkcji array_map. Ale to się nie powiedzie się z powodu błędu

PHP Warning: array_map(): Argument nr 2 powinien być tablicą

można to osiągnąć poprzez wdrożenie Inne/więcej interfejsów, dzięki czemu Collection obiekty są postrzegane jako tablice?

+3

rolki własną funkcję collection_map? – Adder

+0

@Adder Oczywiście mogę, ale teraz szukam rozwiązania, jeśli mogę używać mojej kolekcji z funkcjami php buildin :) – f1ames

Odpowiedz

6

array_map chce, jak sama nazwa wskazuje, tablice. W końcu nie nazywa się to iterator_map. ;)

Oprócz iterator_to_array(), który tworzy potencjalnie dużą tymczasową tablicę, nie ma żadnej sztuczki, która umożliwiłaby pracę obiektów z array_map.

Biblioteka Functional PHP ma implementację map, która działa na dowolnej kolekcji iteracji.

+0

Implementacja funkcjonalnej mapy PHP nie jest wydajna pod względem pamięci: wyniki są przechowywane w tablicy. Znalazłem lepszą bibliotekę: https://github.com/SuRaMoN/itertools I wpis na blogu wyjaśniający, jak możesz ją zbudować samodzielnie: http://www.a-basketful-of-papayas.net/2012/07/what -iterators-can-do-you-you.html –

+0

Aad, ogólnie rzecz biorąc wynikiem funkcji map jest * nowa * tablica - narzut pamięci jest wrodzony do podejścia i jest znikomy w większości przypadków użycia. –

+0

"Nie ma żadnej sztuczki, która powodowałaby, że obiekty iterowalne działałyby z' tablica_mapowa. " Ta sztuczka to 'iterator_to_array()'. –

19

Funkcja array_map() nie obsługuje Traversable jako jego tablicy argumentu, więc trzeba by wykonać krok konwersji:

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

oprócz Iterowanie nad kolekcji dwa razy, ale również wydajność tablicę, która nie będzie użyj później.

Innym sposobem jest napisać własną funkcję map:

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

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

    return $result; 
} 

Aktualizacja

Sądząc po twojej użytkowej przypadku wydaje się, że nawet nie jesteś zainteresowany w wyniku operacji mapie ; dlatego bardziej sensowne jest używanie iterator_apply().

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

    return true; 
}); 
+1

To działa, ale ma obniżenie wydajności, ponieważ będzie iterować podczas kroku iterator_to_array, a będzie ono powtarzać ponownie podczas kroku array_map. –

+1

@EelkevandenBos Dałem dwa rozwiązania w mojej odpowiedzi, te ostatnie nie wykazujące tego "kary za wyniki"; poza tym w obu przypadkach środowisko wykonawcze to O (n). –

2

wymyśliłem następujące rozwiązanie:

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

ten sposób można wygenerować tablicę bez iteracji dwukrotnie byś aby z podejściem jak:

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

Dobry szczęście!

3

Jeśli jesteś nie zainteresowany tworzeniem nowej tablicy, która jest funkcją odwzorowaną na oryginalną tablicę, możesz po prostu użyć pętli foreach (ponieważ implementujesz Iterator).

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

jeśli rzeczywiście chcesz użyć mapy, to myślę, że będziesz musiał wdrożyć własną.Sugerowałbym, dzięki czemu metoda na Collection, np:

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

Chciałbym zadać sobie pytanie, czy naprawdę chcesz używać map czy ty naprawdę tak myśli foreach

Powiązane problemy