Differenza tra array_map, array_walk e array_filter


373

Qual è esattamente la differenza tra array_map, array_walke array_filter. Quello che ho potuto vedere dalla documentazione è che potresti passare una funzione di callback per eseguire un'azione sull'array fornito. Ma non sembra trovare alcuna differenza particolare tra di loro.

Eseguono la stessa cosa?
Possono essere usati in modo intercambiabile?

Gradirei il tuo aiuto con esempi illustrativi se differiscono affatto.


Questo è un bel trucco per l'elaborazione denominata dell'array tramite array_reduce (). Vale la pena leggere se stai studiando array_map, array_walk e array_filter. stackoverflow.com/questions/11563119/…
Lance Cleveland

Risposte:


564
  • Modifica dei valori:
    • array_mapimpossibile modificare i valori all'interno degli array di input mentre array_walkcan; in particolare, array_mapnon cambia mai i suoi argomenti.
  • Accesso alle chiavi dell'array:
  • Valore di ritorno:
    • array_maprestituisce un nuovo array, array_walkrestituisce solo true. Pertanto, se non si desidera creare un array a seguito del passaggio di un array, è necessario utilizzare array_walk.
  • Iterazione di più array:
    • array_mappuò anche ricevere un numero arbitrario di array e può iterare su di essi in parallelo, mentre array_walkopera solo su uno.
  • Passare dati arbitrari al callback:
    • array_walkpuò ricevere un parametro arbitrario aggiuntivo da passare al callback. Ciò è per lo più irrilevante da quando PHP 5.3 (quando sono state introdotte funzioni anonime ).
  • Lunghezza della matrice restituita:
    • L'array risultante di array_mapha la stessa lunghezza di quello dell'array di input più grande; array_walknon restituisce un array ma allo stesso tempo non può modificare il numero di elementi dell'array originale; array_filterseleziona solo un sottoinsieme degli elementi dell'array in base a una funzione di filtro. Conserva le chiavi.

Esempio:

<pre>
<?php

$origarray1 = array(2.4, 2.6, 3.5);
$origarray2 = array(2.4, 2.6, 3.5);

print_r(array_map('floor', $origarray1)); // $origarray1 stays the same

// changes $origarray2
array_walk($origarray2, function (&$v, $k) { $v = floor($v); }); 
print_r($origarray2);

// this is a more proper use of array_walk
array_walk($origarray1, function ($v, $k) { echo "$k => $v", "\n"; });

// array_map accepts several arrays
print_r(
    array_map(function ($a, $b) { return $a * $b; }, $origarray1, $origarray2)
);

// select only elements that are > 2.5
print_r(
    array_filter($origarray1, function ($a) { return $a > 2.5; })
);

?>
</pre>

Risultato:

Array
(
    [0] => 2
    [1] => 2
    [2] => 3
)
Array
(
    [0] => 2
    [1] => 2
    [2] => 3
)
0 => 2.4
1 => 2.6
2 => 3.5
Array
(
    [0] => 4.8
    [1] => 5.2
    [2] => 10.5
)
Array
(
    [1] => 2.6
    [2] => 3.5
)

3
Il manuale di PHP dice: "array_walk (): solo i valori dell'array possono essere potenzialmente modificati;"
feeela,

10
"array_map non può funzionare con i tasti array" questo non è vero:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski

12
Non sta ancora accedendo alle chiavi di alcun array, ma sta aumentando i valori inseriti in un array creato dalle chiavi. È una soluzione alternativa, non nega l'affermazione.
inarilo,

mentre array_map non modifica implicitamente i valori, assegnando il risultato allo stesso array lo modifica sostanzialmente, e 'paradossalmente' array_walk che opera sullo stesso array stesso non cambierà i suoi valori direttamente, a meno che non venga passato il valore per riferimento (array walk potrebbe rimuovere gli indici / elementi come array_filter indirettamente tramite una clausola anonima di utilizzo delle funzioni che passa l'array originale ma è una soluzione alternativa). Per concludere, cambiare i valori, né se un valore viene restituito o passato per riferimento ha effettivamente una differenza minore, ma l'array walk funziona con gli indici e la mappa dell'array con più array
FantomX1,

inoltre sembra che non importa che array walk prenda come riferimento il primo parametro array, quando si vuole cambiarlo deve passare anche il valore dell'elemento callback come riferimento
FantomX1

91

L'idea di mappare una funzione su un array di dati deriva dalla programmazione funzionale. Non dovresti pensare array_mapa un foreachloop che chiama una funzione su ogni elemento dell'array (anche se è così che viene implementato). Dovrebbe essere considerato come l'applicazione indipendente della funzione a ciascun elemento dell'array.

In teoria, cose come la mappatura delle funzioni possono essere fatte in parallelo poiché la funzione applicata ai dati dovrebbe influenzare SOLO i dati e NON lo stato globale. Questo perché si array_mappotrebbe scegliere qualsiasi ordine in cui applicare la funzione agli oggetti (anche se in PHP non lo fa).

array_walkd'altra parte, l'esatto approccio opposto alla gestione di array di dati. Invece di gestire ogni elemento separatamente, usa uno stato ( &$userdata) e può modificare l'elemento sul posto (proprio come un ciclo foreach). Poiché ogni volta che un articolo viene $funcnameapplicato ad esso, potrebbe cambiare lo stato globale del programma e quindi richiede un unico modo corretto di elaborare gli articoli.

Di nuovo in PHP, array_mape array_walksono quasi identici, tranne che array_walkti dà un maggiore controllo sull'iterazione dei dati e viene normalmente utilizzato per "modificare" i dati sul posto anziché restituire un nuovo array "modificato".

array_filterè davvero un'applicazione di array_walk(o array_reduce) e più o meno fornita solo per comodità.


5
+1 per la comprensione del secondo paragrafo di "In teoria cose come la mappatura di funzioni possono essere fatte in parallelo poiché la funzione applicata ai dati dovrebbe influenzare SOLO i dati e NON lo stato globale". Per noi programmatori paralleli, questa è una cosa utile da tenere a mente.
etherice

Puoi spiegare come array_filter()può essere implementato usando array_walk()?
pfrenssen,

40

Dalla documentazione,

bool array_walk (array & $ array, callback $ funcname [, mixed $ userdata]) <-return bool

array_walk prende un array e una funzione Fe lo modifica sostituendo ogni elemento x con F(x).

array array_map (callback $ callback, array $ arr1 [, array $ ...]) <- restituisce array

array_map fa esattamente la stessa cosa, tranne per il fatto che invece di modificare sul posto restituirà un nuovo array con gli elementi trasformati.

array array_filter (array $ input [, callback $ callback]) <- restituisce array

array_filter con funzione F, invece di trasformare gli elementi, rimuoverà tutti gli elementi per i quali F(x)non è vero


Impossibile capire perché i miei valori di array sono scomparsi. Guardando la documentazione, supponevo di aver array_walkrestituito un array simile array_mape ho pensato che il problema fosse nella mia funzione. Non mi sono reso conto fino a quando non ho visto questo che il tipo restituito è booleano.
Dylan Valade,

22

Le altre risposte dimostrano abbastanza bene la differenza tra array_walk (modifica sul posto) e array_map (ritorno copia modificata). Tuttavia, in realtà non menzionano array_reduce, che è un modo illuminante per comprendere array_map e array_filter.

La funzione array_reduce accetta un array, una funzione a due argomenti e un 'accumulatore', come questo:

array_reduce(array('a', 'b', 'c', 'd'),
             'my_function',
             $accumulator)

Gli elementi dell'array vengono combinati con l'accumulatore uno alla volta, utilizzando la funzione fornita. Il risultato della chiamata sopra è lo stesso di fare questo:

my_function(
  my_function(
    my_function(
      my_function(
        $accumulator,
        'a'),
      'b'),
    'c'),
  'd')

Se preferisci pensare in termini di loop, è come fare il seguente (l'ho effettivamente usato come fallback quando array_reduce non era disponibile):

function array_reduce($array, $function, $accumulator) {
  foreach ($array as $element) {
    $accumulator = $function($accumulator, $element);
  }
  return $accumulator;
}

Questa versione in loop chiarisce perché ho chiamato il terzo argomento un "accumulatore": possiamo usarlo per accumulare risultati attraverso ogni iterazione.

Cosa c'entra questo con array_map e array_filter? Si scopre che sono entrambi un particolare tipo di array_reduce. Possiamo implementarli in questo modo:

array_map($function, $array)    === array_reduce($array, $MAP,    array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())

Ignora il fatto che array_map e array_filter accettano i loro argomenti in un ordine diverso; è solo un'altra stranezza di PHP. Il punto importante è che il lato destro è identico ad eccezione delle funzioni che ho chiamato $ MAP e $ FILTER. Quindi, che aspetto hanno?

$MAP = function($accumulator, $element) {
  $accumulator[] = $function($element);
  return $accumulator;
};

$FILTER = function($accumulator, $element) {
  if ($function($element)) $accumulator[] = $element;
  return $accumulator;
};

Come puoi vedere, entrambe le funzioni accettano l'accumulatore $ e lo restituiscono di nuovo. Esistono due differenze in queste funzioni:

  • $ MAP verrà sempre aggiunto a $ accumulator, ma $ FILTER lo farà solo se $ function ($ element) è TRUE.
  • $ FILTER aggiunge l'elemento originale, ma $ MAP aggiunge la funzione $ ($ element).

Nota che questo è tutt'altro che inutile banalità; possiamo usarlo per rendere i nostri algoritmi più efficienti!

Spesso vediamo codice come questi due esempi:

// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))

// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')

L'uso di array_map e array_filter al posto dei loop rende questi esempi piuttosto belli. Tuttavia, può essere molto inefficiente se $ input è grande, poiché la prima chiamata (mappa o filtro) attraverserà $ input e costruirà un array intermedio. Questo array intermedio viene passato direttamente alla seconda chiamata, che attraverserà di nuovo tutto, quindi l'array intermedio dovrà essere raccolto.

Possiamo sbarazzarci di questo array intermedio sfruttando il fatto che array_map e array_filter sono entrambi esempi di array_reduce. Combinandoli, dobbiamo attraversare $ input solo una volta in ciascun esempio:

// Transform valid inputs
array_reduce($inputs,
             function($accumulator, $element) {
               if (valid($element)) $accumulator[] = transform($element);
               return $accumulator;
             },
             array())

// Get all numeric IDs
array_reduce($inputs,
             function($accumulator, $element) {
               $id = get_id($element);
               if (is_numeric($id)) $accumulator[] = $id;
               return $accumulator;
             },
             array())

NOTA: Le mie implementazioni di array_map e array_filter sopra non si comporteranno esattamente come quelle di PHP, poiché la mia array_map può gestire solo un array alla volta e il mio array_filter non utilizzerà "vuoto" come sua funzione $ predefinita. Inoltre, nessuno dei due manterrà le chiavi.

Non è difficile farli comportare come quelli di PHP, ma ho sentito che queste complicazioni avrebbero reso più difficile individuare l'idea di base.


1

La seguente revisione cerca di delineare più chiaramente array_filer (), array_map () e array_walk () di PHP, tutti originati dalla programmazione funzionale:

array_filter () filtra i dati, producendo di conseguenza un nuovo array che contiene solo gli elementi desiderati del precedente array, come segue:

<?php
$array = array(1, "apples",2, "oranges",3, "plums");

$filtered = array_filter( $array, "ctype_alpha");
var_dump($filtered);
?>

codice live qui

Tutti i valori numerici vengono filtrati da $ array, lasciando $ filtrati solo con tipi di frutta.

array_map () crea anche un nuovo array, ma a differenza di array_filter () l'array risultante contiene tutti gli elementi dell'input $ filtrato ma con valori modificati, a causa dell'applicazione di un callback a ciascun elemento, come segue:

<?php

$nu = array_map( "strtoupper", $filtered);
var_dump($nu);
?>

codice live qui

Il codice in questo caso applica un callback usando strtoupper () incorporato ma anche una funzione definita dall'utente è un'altra opzione praticabile. Il callback si applica a ogni elemento di $ filtrato e genera quindi $ nu i cui elementi contengono valori maiuscoli.

Nel frammento successivo, array walk () attraversa $ nu e apporta modifiche a ciascun elemento rispetto all'operatore di riferimento '&'. Le modifiche si verificano senza creare un array aggiuntivo. Il valore di ogni elemento cambia in una stringa più informativa specificando la sua chiave, categoria e valore.

<?php

$f = function(&$item,$key,$prefix) {
    $item = "$key: $prefix: $item";
}; 
array_walk($nu, $f,"fruit");
var_dump($nu);    
?>    

Vedi la demo

Nota: la funzione di callback rispetto a array_walk () accetta due parametri che acquisiranno automaticamente il valore di un elemento e la sua chiave e anche in quell'ordine, quando invocato da array_walk (). (Vedi di più qui ).


1
Si noti che le funzioni $lambdae $callbacksono solo eta-espansioni di funzioni esistenti e sono quindi completamente ridondanti. Puoi ottenere lo stesso risultato passando (il nome di) la funzione sottostante: $filtered = array_filter($array, 'ctype_alpha');e$nu = array_map('strtoupper', $filtered);
Warbo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.