Scritto da Reinder de Vries, il 9 luglio 2020 in Sviluppo App, Swift
In Swift si utilizza map()
, reduce()
e filter()
per eseguire un ciclo di collezioni come matrici e dizionari, senza l’utilizzo di un ciclo for.
Le funzioni mappa, riduzione e filtro provengono dal regno della programmazione funzionale (FP). Sono chiamate funzioni di ordine superiore, perché prendono funzioni come input. Si sta applicando una funzione a un array, ad esempio, per trasformare i suoi dati.
Mappa di Swift, Ridurre e filtrare le funzioni possono impegnativo per avvolgere la testa intorno. Soprattutto se hai sempre codificato for in
loop per risolvere i problemi di iterazione. In questa guida imparerai come utilizzare le funzioni map(_:)
, reduce(_:_:)
e filter(_:)
in Swift.
Iniziamo!
- Introduzione a Mappare, ridurre e filtrare
- Avvio rapido: Funzioni di ordine superiore in Swift
- Utilizzo della funzione Mappa
- Utilizzo della funzione Riduci
- Utilizzo della funzione Filtro
- Combinazione di mappa, Riduzione e filtro
- Ulteriori letture
Introduzione a Mappa, Riduzione e filtro
Quando si crea iOS applicazioni, in genere si utilizza la programmazione procedurale o orientata agli oggetti. La programmazione funzionale è diversa: si occupa solo di funzioni. Nessuna variabile, nessuno stato, nessun for-loop-solo funzioni.
Il linguaggio di programmazione Swift si presta perfettamente per mescolare la programmazione funzionale con approcci non funzionali, come OOP. Non è necessario scrivere rigorosamente codice funzionale e l’adozione di concetti dalla programmazione funzionale può aiutarti a imparare come codificare meglio.
Le funzioni map(_:)
, reduce(_:_:)
e filter(_:)
sono chiamate funzioni di ordine superiore, perché prendono una funzione come input e funzioni di ritorno come output. Tecnicamente, Swift restituisce i risultati di un’operazione (es. un array trasformato) quando si utilizzano funzioni di ordine superiore, mentre un linguaggio funzionale puro restituirà una raccolta di funzioni. In Swift, gli input per queste funzioni sono chiusure.
Ecco come funzionano:
- La funzione
map()
applica una funzione a ogni elemento di una raccolta. Pensa a “mappare” o trasformare un insieme di valori in un altro insieme di valori. - La funzione
reduce()
trasforma una raccolta in un unico valore. Pensate a come combinare molti valori in uno, come la media di un insieme di numeri. - La funzione
filter()
restituisce semplicemente i valori che hanno superato un’istruzioneif
e solo se tale condizione ha portato atrue
.
Nel caso tu stia pensando: “Guarda, non ho bisogno di programmazione funzionale o elaborazione dati, perché le mie app non lo fanno!”allora non fermarti qui. Negli ultimi progetti di app ho usato Map, Reduce e Filter in più occasioni:
- Filtri costi/ricavi valori
filter(_:)
, per soddisfare una soglia, prima che mostra i valori in un grafico a linee - una Media di migliaia di voti filmato in un unico valore con
reduce(_:_:)
- Mapping alcune operazioni su una stringa con hashtags, trasformandolo in un normalizzato di raccolta, con
map(_:)
Si potrebbe avere risolto tutti questi problemi con un ciclo for, ma vedrai che l’utilizzo di map()
, reduce()
e filter()
risultati più conciso, chiaro e performante codice.
Avvio rapido: Funzioni di ordine superiore in Swift
Ci concentreremo su map()
, reduce()
e filter()
in questo tutorial. Prima di andare avanti, ecco una rapida panoramica delle funzioni di ordine superiore più comuni in Swift:
-
map(_:)
loop su ogni elemento in una sequenza, si applica una funzione che ad ogni elemento e restituisce la trasformata risultato -
reduce(_:_:)
loop su ogni elemento in una sequenza, li combina in un unico valore, e restituisce il risultato combinato -
filter(_:)
loop su ogni elemento in una sequenza e restituisce una sequenza risultante che contiene solo gli elementi che soddisfano una determinata funzione di filtraggio -
flatMap(_:)
fa la stessa cosamap(_:)
, tranne che appiattisce la sequenza risultante, cioè gli array nidificati non sono nidificati o “appiattiti” -
compactMap(_:)
fa lo stesso dimap(_:)
, tranne che rimuove i valorinil
dalla sequenza risultante prima di restituirlo
È possibile utilizzare queste funzioni su array, dizionari, set, intervalli, sequenze e qualsiasi altro tipo Swift che è possibile iterare. Se vuoi saperne di più su compactMap(_:)
e flatMap(_:)
, dai un’occhiata a questo tutorial.
Diversi tipi in Swift, come Array e Dictionary, hanno funzioni che accettano chiusure come input. Una scelta rapida:
-
contains(where:)
loop su una raccolta, si applica un predicato (chiusura) per ogni elemento, restituisce untrue
se un elemento soddisfa il predicato, altrimentifalse
-
first(where:)
loop su una raccolta, si applica un predicato (chiusura) per ogni elemento e restituisce l’elemento se soddisfa il predicato -
firstIndex(where:)
fa la stessa cosafirst(where:)
, tranne che restituisce l’indice anziché il valore
Si può imparare di più su queste funzioni in questo tutorial. Anche il modo in cui where
viene utilizzato in Swift è interessante, puoi saperne di più in questo tutorial.
Hai visto le funzioni “mappa” e “riduci” in Swift scritte come map(_:)
e reduce(_:_:)
in questo tutorial. I caratteri di sottolineatura e i due punti in tali funzioni fanno parte della firma della funzione, che è un formato speciale per indicare i parametri della funzione. Ad esempio, la funzione map(_:)
ha un parametro senza nome, mentre la funzione reduce(_:_:)
ne ha due. Puoi saperne di più su questo in questo tutorial: Funzioni in Swift Explained.

Vieni assunto come sviluppatore iOS
Impara a creare app iOS 14 con Swift 5
Iscriviti al mio corso di sviluppo iOS e scopri come iniziare la tua carriera come sviluppatore iOS professionista.
Utilizzando la funzione Mappa
La funzione map(_:)
esegue il loop su ogni elemento di una raccolta e applica un’operazione a ciascun elemento della raccolta. Restituisce una raccolta di elementi risultanti, a cui è stata applicata l’operazione.
Diamo un’occhiata ad un esempio. Abbiamo una serie di temperature in gradi Celsius che si desidera trasformare in Fahrenheit.
Potresti usare un ciclo for, come questo:
let celsius = var fahrenheit: = for value in celsius { fahrenheit += }print(fahrenheit)// Output:
Il codice funziona bene, ma è troppo prolisso. Hai bisogno di una variabile “helper” mutabile fahrenheit
per memorizzare le conversioni calcolate mentre le lavori e hai bisogno di 3 righe di codice per la conversione stessa.
Ecco come possiamo fare lo stesso con la funzione map(_:)
:
lasciate fahrenheit = celsius.mappa { $0 * (9/5) + 32 }
stampa (fahrenheit)
Potresti persino fare tutto questo su una riga:
.map { * (9/5) + 32 }
Cosa succede qui?
- Viene definita una costante
celsius
, una matrice di doppi e inizializzata con alcuni valori Celsius casuali. - La funzione
map(_:)
viene richiamata sull’arraycelsius
. La funzione ha un argomento, una chiusura, che converte da Celsius a Fahrenheit. - Infine, viene stampato il risultato: l’array convertito, da Celsius a Fahrenheit.
La funzione map(_:)
trasforma un array in un altro, applicando una funzione ad ogni elemento dell’array. La chiusura * (9/5) + 32
prende il valore di input in Celsius e restituisce un valore in Fahrenheit. L’array risultante di map(_:)
viene creato da quei valori convertiti.
Diamo un’occhiata più da vicino alla chiusura. Se hai già lavorato con le chiusure, riconoscerai la sintassi di chiusura a mano corta. È un modo più breve per codificare una chiusura, tralasciando gran parte della sua sintassi.
Ecco un’alternativa meno concisa:
let celsius = let fahrenheit = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})print(fahrenheit)
L’effettiva chiamata di funzione map(_:)
e la sua chiusura, è questa:
··· = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})
Che succede li’? La funzione map(_:)
viene chiamata sull’array celsius
. Ci vuole un argomento: una chiusura di tipo (Double) -> Double
.
La prima parte della chiusura, che inizia con {
, indica che questa chiusura ha un parametro value
di tipo Double
e la chiusura restituisce un valore di tipo Double
. Il corpo di chiusura, a partire da return
, restituisce semplicemente il risultato del calcolo da Celsius a Fahrenheit.
Se confronti la sintassi di chiusura a mano corta con il codice espanso sopra, vedrai che:
- Le parentesi di funzione
(
e)
vengono omesse, perché è possibile ometterle quando l’ultimo parametro di una chiamata di funzione è una chiusura. - La parte
() -> in
può essere omessa, perché Swift può dedurre che si sta utilizzando un parametroDouble
come input e ci si aspetta che restituisca unDouble
. Ora che hai lasciato fuori la variabilevalue
, puoi usare la mano corta.
- Anche l’istruzione
return
può essere lasciata fuori, perché questa chiusura dovrebbe comunque restituire il risultato di un’espressione.
Anche se l’esempio di codice precedente utilizza i tipi Double
, non sei limitato a questi tipi. Il tipo risultante di una funzione map()
può avere un tipo diverso da quello che ci si inserisce e si può usare map()
anche su un Dictionary
.
Passiamo a reduce(_:_:)
!
Utilizzando la funzione Riduci
La funzione reduce(_:_:)
esegue il loop su ogni elemento di una raccolta e li riduce a un valore. Pensate a come combinare più valori in uno.
La funzione di riduzione è forse la più difficile di map, reduce, filter da comprendere. Come si può passare da una raccolta di valori, a un valore?
Alcuni esempi:
- Creazione di una somma di più valori, ovvero
3 + 4 + 5 = 12
- Concatenazione di una raccolta di stringhe, ovvero
= "Zaphod, Trillian, Ford"
- Media di un insieme di valori, ovvero
(7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667
Nell’elaborazione dei dati, puoi immaginare molti scenari quando operazioni semplici come queste sono utili. Come prima, puoi risolvere uno di questi problemi con un ciclo for, ma reduce(_:_:)
è semplicemente più breve e veloce.
Ecco come:
let sum = values.riduci (0, +)
stampa (somma)
La funzione reduce(_:_:)
accetta due argomenti, un valore iniziale e una chiusura. Nel codice sopra stiamo fornendo l’operatore +
, che è anche una funzione con due parametri.
Puoi anche fornire la tua chiusura, ovviamente:
let average = values.ridurre(0.0) { $0 + $1 } / Doppio (valori.conteggio)
stampa (media)
Nell’esempio precedente, stai calcolando la media di tre numeri. I tipi di valori nel codice sono tutti Double
. Prima sommiamo tutti i numeri e poi li dividiamo per la quantità di numeri.
La funzione reduce(_:_:)
è diversa in due modi:
- La funzione
reduce(_:_:)
ha due parametri senza nome; il valore iniziale e la chiusura - La chiusura fornita a
reduce(_:_:)
ha anche due parametri; il risultato corrente della riduzione e il nuovo valore che sta per essere ridotto
Qui, controlla questo:
let sum = values.ridurre(0) {
stampa(“\($0) + \($1) = \($0 + $1)”)
ritorno $0 + $1
}
stampa(somma)
Nell’esempio precedente, si può vedere chiaramente e
, il 2 parametri delle chiusure. Quando esegui il codice, questo è l’output che ottieni:
0 + 7 = 77 + 3 = 1010 + 10 = 2020
Vedi come stiamo iniziando con 0
, quindi aggiungendo 7
? Nel passaggio successivo, prendiamo 3, il valore “successivo” nella riduzione.
Ecco un altro modo di guardarlo:
0 + 7(0 + 7) + 3((0 + 7) + 3) + 10
Ciò chiarisce anche perché è necessario un valore iniziale per reduce(_:_:)
, perché questo è il primo primo parametro della chiusura.
La riduzione può essere difficile da afferrare. È importante capire che si sta applicando iterativamente un’operazione, come +
, a un insieme di valori fino a quando non si rimane con un solo valore. Riduci letteralmente la quantità di valori.
Passiamo a filter(_:)
!
Utilizzando la funzione Filtro
La funzione filter
esegue il loop su ogni elemento di una raccolta e restituisce una raccolta contenente solo elementi che soddisfano una condizione di inclusione.
È come applicare un’istruzione if
a una raccolta e mantenere solo i valori che superano la condizione.
Qui, controlla questo:
let even = values.filtro {0 0.isMultiple (di: 2)}
stampa (anche)
Nell’esempio precedente, stai filtrando i numeri da values
che sono pari. La funzione isMultiple(of:)
restituisce true
quando può essere diviso per
2
e false
altrimenti.
A differenza di map(_:)
e reduce(_:_:)
, la chiusura filter(_:)
deve restituire un booleano, quindi true
o false
. Quando la chiusura restituisce true
, il valore viene mantenuto e quando viene restituito false
, il valore viene omesso. Ecco come filter(_:)
filtra l’array di input.
Ecco un esempio leggermente più chiaro, con la chiusura espansa:
let values = let even = values.filter({ (value:Int) -> Bool in return value.isMultiple(of: 2)})print(even) // Output:
Nell’esempio, la chiusura restituisce un valore booleano, indicato con -> Bool
. Viene fornito un parametro, l’elemento nella raccolta, e restituisce il risultato di isMultiple(of:)
. Pulito!
Combinazione di mappa, riduzione e filtro
È possibile combinare le funzioni map()
, reduce()
e filter()
? Certo che puoi!
Diciamo che abbiamo una classe di studenti. Sai l’anno in cui ogni studente è nato. Si desidera calcolare l’età combinata di tutti gli studenti nati nel 2000 o dopo.
Ecco come lo fai:
lasciate anni =
lasciate somma = anni.filtro({ $0 >= 2000 }).mappa ({ora -} 0}).riduci (0, +)
stampa (somma)
L’esempio di codice precedente utilizza il concatenamento. Utilizza il risultato di una funzione come input per un’altra, combinando map-reduce-filter. La funzione map()
viene chiamata sulla matrice dei risultati della funzione filter()
e la funzione reduce()
viene chiamata sul risultato della funzione map()
. Fantastico!
Il codice stesso è semplice:
- Crea una costante
now
eyears
e assegna un sacco di anni ad esso. - Filtrare gli anni inferiori al 2000, ovvero mantieni quelli per cui
>= 2000
ètrue
- Trasforma ogni anno in un’età, sottraendo l’anno da
now
- Aggiungi tutte le età in su, riducendo con
+
Facciamo un esempio più interessante. Controllare il codice seguente, tratto dal tutorial su FizzBuzz:
switch (i % 3 == 0, i % 5 == 0)
{
caso (vero, falso):
ritorno “Fizz”
caso (false, true):
ritorno “Buzz”
caso (true, true):
ritorno “FizzBuzz”
default:
ritorno ” \(i)”
}
}
sia result = Array(2…100).mappa (fizzbuzz).ridurre(“1”, { $0 + “, ” + $1 })
stampa (risultato)
Vedi cosa sta succedendo? Stiamo trasformando un array con numeri da 2 a 100 in “Fizz”, “Buzz” o “FizzBuzz” con map(_:)
, in base alle regole del gioco. Infine, stiamo riducendo quella matrice di stringhe in una grande stringa con reduce(_:_:)
, combinando ogni valore. Pulito!
Ulteriori letture
E se dovessi codificare tutto questo con for in
loop? Useresti molto più codice. E questo è il potere di map-reduce-filter: è più conciso, spesso più facile da leggere, e — ammettilo-dannatamente bello!
Vuoi saperne di più? Dai un’occhiata a queste risorse:
- Come usare” where ” in Swift
- FlatMap e CompactMap Spiegate In Swift
- La guida definitiva alle chiusure in Swift
- Come: trovare un elemento in un array In Swift
- Gioca con il codice: Ricerca binaria in Swift
- Inizia con Xcode Playgrounds

Vieni assunto come sviluppatore iOS
Impara a creare app iOS 14 con Swift 5
Iscriviti al mio corso di sviluppo iOS e scopri come iniziare la tua carriera come sviluppatore iOS professionista.