Geschrieben von Reinder de Vries am 9. Juli 2020 in App-Entwicklung, Schnell

 Map, Reduce und Filter in Swift

In Swift verwenden Sie map(), reduce() und filter() , um Sammlungen wie Arrays und Wörterbücher zu durchlaufen, ohne eine for-Schleife zu verwenden.

Die Map-, Reduce- und Filterfunktionen stammen aus dem Bereich der funktionalen Programmierung (FP). Sie werden Funktionen höherer Ordnung genannt, weil sie Funktionen als Eingabe verwenden. Sie wenden beispielsweise eine Funktion auf ein Array an, um seine Daten zu transformieren.

Swifts Map-, Reduce- und Filterfunktionen können Ihnen helfen, sich zurechtzufinden. Vor allem, wenn Sie immer for in Schleifen codiert haben, um Iterationsprobleme zu lösen. In diesem Handbuch erfahren Sie, wie Sie die Funktionen map(_:), reduce(_:_:) und filter(_:) in Swift verwenden.

Los geht’s!

  1. Einführung in Map, Reduce und Filter
  2. Schnellstart: Funktionen höherer Ordnung in Swift
  3. Verwenden der Map-Funktion
  4. Verwenden der Reduce-Funktion
  5. Verwenden der Filterfunktion
  6. Kombinieren von Map, Reduce und Filter
  7. Weiterführende Literatur

Einführung in Map, Reduce und Filter

Beim Erstellen von iOS apps verwenden Sie normalerweise prozedurale oder objektorientierte Programmierung. Funktionale Programmierung ist anders: Sie befasst sich nur mit Funktionen. Keine Variablen, kein Zustand, keine For-Schleifen – nur Funktionen.

Die Swift-Programmiersprache eignet sich hervorragend zum Mischen von funktionaler Programmierung mit nicht-funktionalen Ansätzen wie OOP. Sie müssen nicht unbedingt funktionalen Code schreiben, und die Übernahme von Konzepten aus der funktionalen Programmierung kann Ihnen helfen, besser zu programmieren.

Die Funktionen map(_:), reduce(_:_:) und filter(_:) werden als Funktionen höherer Ordnung bezeichnet, da sie eine Funktion als Eingabe und Rückgabefunktionen als Ausgabe annehmen. Technisch gibt Swift die Ergebnisse einer Operation zurück (dh. b. ein transformiertes Array), wenn Funktionen höherer Ordnung verwendet werden, während eine reine funktionale Sprache eine Sammlung von Funktionen zurückgibt. In Swift sind die Eingaben für diese Funktionen closures .

So funktionieren sie:

  • Die Funktion map() wendet eine Funktion auf jedes Element in einer Sammlung an. Denken Sie an „Mapping“ oder die Umwandlung eines Satzes von Werten in einen anderen Satz von Werten.
  • Die Funktion reduce() wandelt eine Sammlung in einen Wert um. Stellen Sie sich vor, Sie kombinieren viele Werte zu einem, z. B. den Durchschnitt einer Reihe von Zahlen.
  • Die filter() -Funktion gibt einfach Werte zurück, die eine if -Anweisung übergeben haben, und nur, wenn diese Bedingung zu true geführt hat.

Falls Sie denken: „Schau, ich brauche keine funktionale Programmierung oder Datenverarbeitung, weil meine Apps das nicht tun!“ dann hör hier nicht auf. In den letzten App-Projekten habe ich Map, Reduce und Filter mehrfach verwendet:

  • Filtern von Kosten- / Umsatzwerten mit filter(_:), um einen Schwellenwert zu erreichen, bevor die Werte in einem Liniendiagramm angezeigt werden
  • Durchschnittlich Tausende von Filmbewertungen zu einem Wert mit reduce(_:_:)
  • Zuordnung einiger Operationen zu einer Zeichenfolge mit Hashtags, Umwandlung in eine normalisierte Sammlung, mit map(_:)

Sie hätten all diese Probleme mit einer for-Schleife lösen können, aber Sie werden sehen, dass die Verwendung von map() , reduce() und filter() zu einem prägnanteren, lesbareren und performanteren Code führt.

Schnellstart: Funktionen höherer Ordnung in Swift

In diesem Tutorial konzentrieren wir uns auf map(), reduce() und filter(). Bevor wir fortfahren, finden Sie hier einen kurzen Überblick über die häufigsten Funktionen höherer Ordnung in Swift:

  • map(_:) schleifen über jedes Element in einer Sequenz, wendet eine Funktion auf jedes Element an und gibt das transformierte Ergebnis zurück
  • reduce(_:_:) Schleifen über jedes Element in einer Sequenz, kombiniert sie zu einem Wert und gibt das kombinierte Ergebnis zurück
  • filter(_:) Schleifen über jedes Element in einer Sequenz und gibt eine resultierende Sequenz zurück, die nur Elemente enthält, die eine bestimmte Filterfunktion erfüllen
  • flatMap(_:) macht das gleiche wie map(_:), außer dass es die resultierende Sequenz abflacht, d.h. verschachtelte Arrays sind nicht verschachtelt oder „abgeflacht“
  • compactMap(_:) macht das gleiche wie map(_:), außer dass es nil Werte aus der resultierenden Sequenz entfernt, bevor es zurückgegeben wird

Sie können diese Funktionen für Arrays, Wörterbücher, Mengen, Bereiche, Sequenzen und jeden anderen Swift-Typ verwenden, über den Sie iterieren können. Wenn Sie mehr über compactMap(_:) und flatMap(_:) erfahren möchten, lesen Sie dieses Tutorial.

Mehrere Typen in Swift, wie Array und Dictionary , haben Funktionen, die Schließungen als Eingabe akzeptieren. Eine schnelle Auswahl:

  • contains(where:) schleifen über eine Sammlung, wendet ein Prädikat (einen Abschluss) auf jedes Element an, gibt ein true zurück, wenn ein Element das Prädikat erfüllt, andernfalls false
  • first(where:) schleifen über eine Sammlung, wendet ein Prädikat (einen Abschluss) auf jedes Element an und gibt das Element zurück, wenn es das Prädikat erfüllt
  • firstIndex(where:) macht dasselbe wie first(where:), außer dass es den Index anstelle des Werts zurückgibt

In diesem Lernprogramm erfahren Sie mehr über diese Funktionen. Wie where in Swift verwendet wird, ist ebenfalls interessant.

In diesem Lernprogramm wurden die Funktionen „map“ und „reduce“ in Swift als map(_:) und reduce(_:_:) geschrieben. Die Unterstriche und Doppelpunkte in diesen Funktionen sind Teil der Signatur der Funktion, einem speziellen Format zur Angabe von Funktionsparametern. Zum Beispiel hat die Funktion map(_:) einen unbenannten Parameter, während die Funktion reduce(_:_:) zwei hat. Mehr dazu erfahren Sie in diesem Tutorial: Funktionen in Swift erklärt.

Als iOS-Entwickler eingestellt werden

Lernen Sie, iOS 14-Apps mit Swift 5 zu erstellen

Melden Sie sich für meinen iOS-Entwicklungskurs an und erfahren Sie, wie Sie Ihre Karriere als professioneller iOS-Entwickler beginnen können.

Verwenden der Map-Funktion

Die Funktion map(_:) durchläuft jedes Element in einer Sammlung und wendet eine Operation auf jedes Element in der Sammlung an. Es gibt eine Sammlung von resultierenden Elementen zurück, auf die die Operation angewendet wurde.

Schauen wir uns ein Beispiel an. Wir haben eine Reihe von Temperaturen in Celsius, die Sie in Fahrenheit umwandeln möchten.

Sie könnten eine for-Schleife wie diese verwenden:

let celsius = var fahrenheit: = for value in celsius { fahrenheit += }print(fahrenheit)// Output: 

Der Code funktioniert gut, ist aber zu ausführlich. Sie benötigen eine veränderliche „Helfer“ -Variable fahrenheit, um die berechneten Konvertierungen zu speichern, während Sie sie durcharbeiten, und Sie benötigen 3 Codezeilen für die Konvertierung selbst.

So können wir dasselbe mit der Funktion map(_:) tun:

sei Celsius =
sei Fahrenheit = Celsius.karte { $0 * (9/5) + 32 }
drucken (fahrenheit)
Warnungen ausblenden

Sie könnten das alles sogar in einer Zeile tun:

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

Was passiert hier?

  1. Eine Konstante celsius wird definiert, ein Array von Doubles, und mit einigen zufälligen Konstanten Werten initialisiert.
  2. Die Funktion map(_:) wird für das Array celsius aufgerufen. Die Funktion hat ein Argument, einen Abschluss, der von Celsius in Fahrenheit konvertiert.
  3. Schließlich wird das Ergebnis ausgedruckt: das konvertierte Array von Celsius in Fahrenheit.

Die Funktion map(_:) transformiert ein Array in ein anderes, indem sie eine Funktion auf jedes Element im Array anwendet. Der closure * (9/5) + 32 nimmt den Eingabewert in Celsius und gibt einen Wert in Fahrenheit zurück. Das resultierende Array von map(_:) wird aus diesen konvertierten Werten aufgebaut.

Schauen wir uns die Schließung genauer an. Wenn Sie schon einmal mit Closures gearbeitet haben, erkennen Sie die Syntax für Short-Hand-Closures. Es ist eine kürzere Möglichkeit, einen Abschluss zu codieren, indem ein Großteil seiner Syntax weggelassen wird.

Hier ist eine weniger prägnante Alternative:

let celsius = let fahrenheit = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})print(fahrenheit)

Der eigentliche map(_:) Funktionsaufruf und seine Schließung ist dies:

··· = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})

Was ist da los? Die Funktion map(_:) wird für das Array celsius aufgerufen. Es braucht ein Argument: eine Schließung vom Typ (Double) -> Double .

Der erste Teil der Schließung, beginnend mit {, gibt an, dass diese Schließung einen Parameter value vom Typ Double hat, und die Schließung gibt einen Wert vom Typ Double zurück. Der Schließkörper, beginnend mit return, gibt einfach das Ergebnis der Celsius-Fahrenheit-Berechnung zurück.

Wenn Sie die Short-Hand-Closure-Syntax mit dem erweiterten Code oben vergleichen, werden Sie Folgendes sehen:

  • Die Funktionsklammern ( und ) werden weggelassen, da Sie diese weglassen können, wenn der letzte Parameter eines Funktionsaufrufs ein Abschluss ist.
  • Der () -> in -Teil kann weggelassen werden, da Swift daraus schließen kann, dass Sie einen Double -Parameter als Eingabe verwenden und von dem erwartet wird, dass er einen Double zurückgibt. Nachdem Sie die Variable value weggelassen haben, können Sie die Kurzhand .
  • Die Anweisung return kann ebenfalls weggelassen werden, da von diesem Abschluss erwartet wird, dass er das Ergebnis eines Ausdrucks zurückgibt.

Obwohl das obige Codebeispiel Double -Typen verwendet, sind Sie nicht auf diese Typen beschränkt. Der resultierende Typ einer map() -Funktion kann einen anderen Typ haben als der, den Sie eingeben, und Sie können map() auch für Dictionary verwenden.

Gehen wir weiter zu reduce(_:_:)!

Verwenden der Reduce-Funktion

Die Funktion reduce(_:_:) durchläuft jedes Element in einer Sammlung und reduziert es auf einen Wert. Stellen Sie sich vor, Sie kombinieren mehrere Werte zu einem.

Die Reduce-Funktion ist vielleicht die am schwersten zu verstehende von map, reduce, filter . Wie können Sie von einer Sammlung von Werten zu einem Wert wechseln?

Einige Beispiele:

  • Erstellen einer Summe mehrerer Werte, dh 3 + 4 + 5 = 12
  • Verketten einer Sammlung von Zeichenfolgen, dh = "Zaphod, Trillian, Ford"
  • Mittelwertbildung einer Reihe von Werten, dh. (7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667

In der Datenverarbeitung können Sie sich viele Szenarien vorstellen, in denen einfache Operationen wie diese nützlich sind. Wie zuvor können Sie jedes dieser Probleme mit einer for-Schleife lösen, aber reduce(_:_:) ist einfach kürzer und schneller.

So geht’s:

sei Werte =
sei Summe = Werte.reduce(0, +)
drucken(Summe)
Warnungen ausblenden

Die Funktion reduce(_:_:) benötigt zwei Argumente, einen Anfangswert und einen Abschluss. Im obigen Code stellen wir den Operator + bereit, der auch eine Funktion mit zwei Parametern ist.

Sie können natürlich auch Ihren eigenen Verschluss angeben:

sei Werte =
sei Durchschnitt = Werte.reduzieren(0.0) { $0 + $1 } / Double(Werte.zählen)
drucken (durchschnitt)
Warnungen ausblenden

Im obigen Beispiel berechnen Sie den Durchschnitt von drei Zahlen. Die Typen der Werte im Code sind alle Double . Wir addieren zuerst alle Zahlen und dividieren sie dann durch die Anzahl der Zahlen.

Die Funktion reduce(_:_:) unterscheidet sich auf zwei Arten:

  1. Die Funktion reduce(_:_:) hat zwei unbenannte Parameter; den Anfangswert und den Abschluss
  2. Der Abschluss, der für reduce(_:_:) bereitgestellt wird, hat ebenfalls zwei Parameter; das aktuelle Ergebnis der Reduzierung und der neue Wert, der reduziert werden soll

Überprüfen Sie dies hier:

sei Werte =
sei Summe = Werte.reduzieren(0) {
drucken(„\($0) + \($1) = \($0 + $1)“)
rückkehr $0 + $1
}
drucken(Summe)
Warnungen ausblenden

Im obigen Beispiel sehen Sie deutlich und , die 2 Parameter der Verschlüsse. Wenn Sie den Code ausführen, erhalten Sie folgende Ausgabe:

0 + 7 = 77 + 3 = 1010 + 10 = 2020

Sehen Sie, wie wir mit 0 beginnen und dann 7 hinzufügen? Im nächsten Schritt nehmen wir 7 – den aktuellen Reduktionswert – und addieren 3, den „nächsten“ Wert in der Reduktion.

Hier ist eine andere Sichtweise:

0 + 7(0 + 7) + 3((0 + 7) + 3) + 10

Dies macht auch deutlich, warum Sie einen Anfangswert für reduce(_:_:) benötigen, da dies der erste erste Parameter des Abschlusses ist.

Reduktion kann schwierig zu erfassen sein. Es ist wichtig zu verstehen, dass Sie eine Operation wie + iterativ auf eine Reihe von Werten anwenden, bis nur noch ein Wert übrig bleibt. Sie reduzieren buchstäblich die Anzahl der Werte.

Weiter zu filter(_:)!

Verwenden der Filterfunktion

Die Funktion filter durchläuft jedes Element in einer Sammlung und gibt eine Sammlung zurück, die nur Elemente enthält, die eine Include-Bedingung erfüllen.

Es ist, als würde man eine if -Anweisung auf eine Sammlung anwenden und nur die Werte beibehalten, die die Bedingung übergeben.

Hier, schau dir das an:

sei values =
sei even = values.filter { $0.isMultiple(of: 2) }
drucken(gerade)
Warnungen ausblenden

Im obigen Beispiel filtern Sie gerade Zahlen aus values. Die Funktion isMultiple(of:) gibt true zurück, wenn durch 2 geteilt werden kann, andernfalls false.

Im Gegensatz zu map(_:) und reduce(_:_:) muss der Abschluss filter(_:) einen booleschen Wert zurückgeben, also entweder true oder false. Wenn der Abschluss true zurückgibt, wird der Wert beibehalten, und wenn false zurückgegeben wird, wird der Wert weggelassen. So filtert filter(_:) das Eingabearray.

Hier ist ein etwas klareres Beispiel, wobei der Verschluss erweitert wird:

let values = let even = values.filter({ (value:Int) -> Bool in return value.isMultiple(of: 2)})print(even) // Output: 

Im Beispiel gibt der Abschluss einen booleschen Wert zurück, der mit -> Bool angegeben ist. Es wird ein Parameter bereitgestellt, das Element in der Auflistung, und das Ergebnis von isMultiple(of:) . Ordentlich!

Kombinieren von Map, Reduce und Filter

Können Sie die Funktionen map(), reduce() und filter() kombinieren? Sicher kannst du!

Nehmen wir an, wir haben eine Klasse von Studenten. Sie wissen, in welchem Jahr jeder Schüler geboren wurde. Sie möchten das kombinierte Alter aller Schüler berechnen, die in oder nach 2000 geboren wurden.

So machen Sie das:

sei jetzt = 2020
sei Jahre =
sei Summe = Jahre.filtern({ $0 >= 2000 }).karte({ jetzt – $0 }).reduce(0, +)
drucken(Summe)
Warnungen ausblenden

Das obige Codebeispiel verwendet Verkettung. Es verwendet das Ergebnis einer Funktion als Eingabe für eine andere und kombiniert map-Reduce-filter. Die Funktion map() wird für das Ergebnisarray der Funktion filter() und die Funktion reduce() für das Ergebnis der Funktion map() aufgerufen. Großartig!

Der Code selbst ist einfach:

  1. Machen Sie eine Konstante now und years und weisen Sie ihr eine Reihe von Jahren zu.
  2. Filtern Sie die Jahre heraus, die unter 2000 liegen, d.h. behalten Sie diejenigen bei, für die >= 2000 true
  3. Jedes Jahr in ein Alter umwandeln, indem Sie das Jahr von now
  4. Addieren Sie alle Altersgruppen, indem Sie mit +

Nehmen wir ein interessanteres Beispiel. Schauen Sie sich den folgenden Code aus dem Tutorial über FizzBuzz an:

lassen Sie fizzbuzz:(Int) -> String = { i in
switch (i % 3 == 0, i % 5 == 0)
{
case (true, false):
Rückgabe „Fizz“
case (false, true):
Rückgabe „Buzz“
case (true, true):
Rückgabe „FizzBuzz“
Standard:
Rückgabe „\(i)“
}
}
sei Ergebnis = Array(2…100).karte(fizzbuzz).reduzieren(„1“, { $0 + „, “ + $1 })
drucken(Ergebnis)
Warnungen ausblenden

Sehen Sie, was los ist? Wir transformieren ein Array mit Zahlen von 2 bis 100 entweder in „Fizz“, „Buzz“ oder „FizzBuzz“ mit map(_:), basierend auf den Spielregeln. Schließlich reduzieren wir dieses Array von Zeichenfolgen zu einer großen Zeichenfolge mit reduce(_:_:) und kombinieren jeden Wert. Ordentlich!

Weiterführende Literatur

Was wäre, wenn Sie all dies mit for in Schleifen codieren müssten? Sie würden viel mehr Code verwenden. Und das ist die Kraft von map-reduce-filter: Es ist prägnanter, oft einfacher zu lesen und — gib es zu — verdammt cool!

Möchten Sie mehr erfahren? Schauen Sie sich diese Ressourcen an:

  • Verwendung von „where“ in Swift
  • flatMap und CompactMap in Swift erklärt
  • Der ultimative Leitfaden für Closures in Swift
  • So finden Sie ein Element in einem Array in Swift
  • Spielen Sie mit Code: Binäre Suche in Swift
  • Erste Schritte mit Xcode Playgrounds

Als iOS-Entwickler eingestellt werden

Lernen Sie, iOS 14-Apps mit Swift 5 zu erstellen

Melden Sie sich für meinen iOS-Entwicklungskurs an und erfahren Sie, wie Sie Ihre Karriere als professioneller iOS-Entwickler beginnen können.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.