2013-04-29 10 views
18

Ich habe eine Klasse namens Collection, die Objekte des gleichen Typs speichert. Collection implementiert Array-Schnittstellen: Iterator, ArrayAccess, und Countable.array_map bei der Sammlung mit Array-Schnittstellen?

Ich würde gerne ein Collection Objekt als Array-Argument an die array_map Funktion übergeben. Aber das schlägt mit dem Fehler

PHP Warning: array_map(): Argument # 2 sollte ein Array sein

Kann ich dies erreichen, indem andere/weitere Schnittstellen Implementierung, so dass Collection Objekte gesehen werden als Arrays?

+3

Ihre eigene Rolle collection_map Funktion? – Adder

+0

@Adder Course kann ich, aber jetzt suche ich nach Lösung, wenn ich meine Sammlung mit buildin PHP-Funktionen verwenden kann :) – f1ames

Antwort

6

array_map will, wie der Name schon sagt, Arrays. Es heißt schließlich nicht iterator_map. ;)

Abgesehen von iterator_to_array(), die ein potenziell großes temporäres Array erzeugt, gibt es keinen Trick, iterierbare Objekte mit array_map arbeiten zu lassen.

Die Functional PHP-Bibliothek verfügt über eine map-Implementierung, die mit jeder iterierbaren Sammlung funktioniert.

+0

Die funktionale PHP-Map-Implementierung ist nicht speicher effizient: die Ergebnisse werden in Array gespeichert. Ich habe eine bessere Bibliothek gefunden: https://github.com/SuRaMoN/itertools Und einen Blogeintrag, der erklärt, wie man es selbst bauen kann: http://www.a-basbasful-of-papayas.net/2012/07/what -iterators-can-do-for-you.html –

+0

Aad, im Allgemeinen ist das Ergebnis einer Map-Funktion ein * neues * Array - der Speicher-Overhead ist dem Ansatz angeboren und in der großen Mehrheit der Anwendungsfälle vernachlässigbar. –

+0

"Es gibt keinen Trick, iterierbare Objekte mit' array_map' zu arbeiten. " Dieser Trick ist 'iterator_to_array()'. –

19

Die array_map() Funktion keinen Traversable als Array Argument unterstützen, so dass Sie einen Umwandlungsschritt durchführen müssen:

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

Neben Iterieren über die Sammlung wird zweimal, es auch eine Reihe ergeben, die nicht danach verwendet werden.

Ein anderer Weg ist Ihre eigene Karte Funktion zu schreiben:

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

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

    return $result; 
} 

aktualisiert

Gemessen an Ihrem Anwendungsfall scheint es, dass Sie in dem Ergebnis der Karte Operation nicht einmal daran interessiert sind ; Daher ist es sinnvoller, iterator_apply() zu verwenden.

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

    return true; 
}); 
+1

Das funktioniert, hat aber eine Leistungseinbuße, da es während des iterator_to_array-Schritts iteriert wird und während des array_map-Schritts erneut durchlaufen wird. –

+1

@EelkevandenBos Ich gab zwei Lösungen in meiner Antwort, wobei letztere diese "Leistungsstrafe" nicht aufweist; Außerdem ist in beiden Fällen die Laufzeit O (n). –

2

kam ich auf die folgende Lösung:

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

Auf diese Weise können Sie ein Array erzeugen, ohne Iteration doppelt so Sie mit dem Ansatz, wie zu würde:

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

Gut Glück!

3

Wenn Sie nicht sind interessiert an der Erstellung eines neuen Array, das eine Funktion über das ursprüngliche Array zugeordnet ist, könnten Sie einfach eine foreach-Schleife verwenden (weil Sie Iterator implementieren).

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

Wenn Sie tatsächlich Karte verwenden möchten, dann denke ich, dass Sie Ihre eigenen implementieren müssen.Ich würde vorschlagen, es ein Verfahren auf Sammlung, zum Beispiel machen:

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

Ich würde sich fragen, ob Sie wirklich map verwenden möchten oder Sie nur meinen Sie wirklich foreach

Verwandte Themen