Scritto da Reinder de Vries, il 9 luglio 2020 in Sviluppo App, Swift

Mappa, Ridurre e Filtro in 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!

  1. Introduzione a Mappare, ridurre e filtrare
  2. Avvio rapido: Funzioni di ordine superiore in Swift
  3. Utilizzo della funzione Mappa
  4. Utilizzo della funzione Riduci
  5. Utilizzo della funzione Filtro
  6. Combinazione di mappa, Riduzione e filtro
  7. 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’istruzione ife solo se tale condizione ha portato a true.

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 cosa map(_:), tranne che appiattisce la sequenza risultante, cioè gli array nidificati non sono nidificati o “appiattiti”
  • compactMap(_:) fa lo stesso di map(_:), tranne che rimuove i valori nil 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 un true se un elemento soddisfa il predicato, altrimenti false
  • 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 cosa first(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 celsius =
lasciate fahrenheit = celsius.mappa { $0 * (9/5) + 32 }
stampa (fahrenheit)
Nascondi avvisi

Potresti persino fare tutto questo su una riga:

.map {  * (9/5) + 32 }

Cosa succede qui?

  1. Viene definita una costante celsius, una matrice di doppi e inizializzata con alcuni valori Celsius casuali.
  2. La funzione map(_:) viene richiamata sull’array celsius. La funzione ha un argomento, una chiusura, che converte da Celsius a Fahrenheit.
  3. 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 Doublee 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 parametro Doublecome input e ci si aspetta che restituisca un Double. Ora che hai lasciato fuori la variabile value, 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 values =
let sum = values.riduci (0, +)
stampa (somma)
Nascondi avvisi

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 values =
let average = values.ridurre(0.0) { $0 + $1 } / Doppio (valori.conteggio)
stampa (media)
Nascondi avvisi

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:

  1. La funzione reduce(_:_:) ha due parametri senza nome; il valore iniziale e la chiusura
  2. 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 values =
let sum = values.ridurre(0) {
stampa(“\($0) + \($1) = \($0 + $1)”)
ritorno $0 + $1
}
stampa(somma)
Nascondi le avvertenze

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 values =
let even = values.filtro {0 0.isMultiple (di: 2)}
stampa (anche)
Nascondi avvisi

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 ora = 2020
lasciate anni =
lasciate somma = anni.filtro({ $0 >= 2000 }).mappa ({ora -} 0}).riduci (0, +)
stampa (somma)
Nascondi avvisi

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:

  1. Crea una costante now e years e assegna un sacco di anni ad esso.
  2. Filtrare gli anni inferiori al 2000, ovvero mantieni quelli per cui >= 2000 è true
  3. Trasforma ogni anno in un’età, sottraendo l’anno da now
  4. Aggiungi tutte le età in su, riducendo con +

Facciamo un esempio più interessante. Controllare il codice seguente, tratto dal tutorial su FizzBuzz:

lasciate che fizzbuzz:(Int) -> String = { i in
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)
Nascondi avvisi

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.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.