2013-04-29 15 views
18

Ho una classe chiamata Collection che memorizza oggetti dello stesso tipo. Collection implementa interfacce array: Iterator, ArrayAccess, SeekableIterator e Countable.array_map sulla raccolta con interfacce array?

Mi piacerebbe passare un oggetto Collection come argomento della matrice alla funzione array_map. Ma questo non riesce con l'errore

PHP Warning: array_map(): Argomento # 2 dovrebbe essere un array

Posso raggiungere questo obiettivo mediante l'attuazione di altri più interfacce /, in modo che Collection oggetti sono visti come array?

+3

rotolo la propria funzione collection_map? – Adder

+0

Corso @Adder Posso, ma ora sto cercando una soluzione se posso usare la mia collezione con buildin php funcs :) – f1ames

risposta

6

array_map desidera, come suggerisce il nome, array. Dopotutto non si chiama iterator_map. ;)

Oltre a iterator_to_array(), che produce un array temporaneo potenzialmente grande, non c'è alcun trucco per rendere gli oggetti iterabili funzionanti con array_map.

La libreria Functional PHP ha un'implementazione map che funziona su qualsiasi raccolta iterabile.

+0

L'implementazione della mappa PHP funzionale non è efficiente in termini di memoria: i risultati sono archiviati in array. Ho trovato una libreria migliore: https://github.com/SuRaMoN/itertools e un post sul blog che spiega come è possibile creare da soli: http://www.a-basketful-of-papayas.net/2012/07/what -iteratori-can-do-for-you.html –

+0

Aad, in generale il risultato di una funzione mappa è un * nuovo * array - il sovraccarico della memoria è innato all'approccio ed è trascurabile nella maggior parte dei casi d'uso. –

+0

"Non c'è alcun trucco per far funzionare oggetti iterabili con' array_map'. " Quel trucco è 'iterator_to_array()'. –

19

La funzione array_map() non supporta una Traversable come argomento di matrice, quindi sarebbe necessario eseguire una fase di conversione:

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

Oltre iterazione sulla raccolta due volte, anche cedere una matrice che non sarà essere utilizzato in seguito.

Un altro modo è quello di scrivere la propria funzione mappa:

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

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

    return $result; 
} 

Aggiornamento

A giudicare dal vostro caso d'uso sembra che non sei nemmeno interessati al risultato dell'operazione mappa ; quindi ha più senso usare iterator_apply().

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

    return true; 
}); 
+1

Funziona, ma ha una penalizzazione delle prestazioni perché verrà iterato durante il passaggio iterator_to_array e verrà ripetuto di nuovo durante il passo array_map. –

+1

@EelkevandenBos Ho dato due soluzioni nella mia risposta, quest'ultima non esibendo questa "penalizzazione della performance"; inoltre, in entrambi i casi il tempo di esecuzione è O (n). –

2

mi si avvicinò con la seguente soluzione:

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

In questo modo, è possibile generare un array senza l'iterazione due volte come si farebbe per l'approccio del tipo:

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

Buono fortuna!

3

Se si è non interessati a creare un nuovo array che è una funzione mappata sull'array originale, è possibile utilizzare solo un ciclo foreach (perché si implementa Iterator).

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

se in realtà si desidera utilizzare la mappa, quindi penso che sarà necessario implementare il proprio.Vorrei suggerire che lo rende un metodo sulla collezione, ad esempio:

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

avrei chiedetevi se davvero si vuole utilizzare map o non si ha realmente solo dire foreach