Technologische Neuigkeiten, Bewertungen und Tipps!

FlatMap und CompactMap in Swift erklärt

Hinweis: Der folgende Artikel hilft Ihnen weiter: FlatMap und CompactMap in Swift erklärt

Zurück zum Blog

Swift verfügt über eine Reihe nützlicher Funktionen zum Transformieren von Sammlungen und Sequenzen. In diesem Tutorial zur App-Entwicklung besprechen wir Map(_:), FlatMap(_:) und CompactMap(_:).

Hier ist, worauf wir uns konzentrieren werden:

  1. Wie map(_:) eine Sammlung oder Sequenz transformiert, indem es einen Abschluss darauf anwendet
  2. Wie flatMap(_:) ein Eingabearray nach dem Aufruf von map(_:) reduzieren kann
  3. Wie compactMap(_:) Null aus dem Eingabearray entfernt

In einem früheren Tutorial haben wir besprochen, wie Sie filter(_:) und Reduce(_:) verwenden können. Diese Funktionen höherer Ordnung sind äußerst hilfreich, um Sammlungen auf prägnante und aufschlussreiche Weise umzuwandeln.

Als kurze Auffrischung der Funktionen höherer Ordnung in Swift ist unser Ausgangspunkt die Funktion map(_:). Diese Funktion wendet eine Transformation auf jedes Element in einer Sequenz an, z. B. auf ein Array oder ein Wörterbuch.

Hier ist ein Beispiel:

seien Zahlen = [2, 3, 4, 5]
let result = zahlen.map({ $0 * $0 })

drucken(Ergebnis)

Lassen Sie uns das aufschlüsseln:

Zuerst erstellen wir ein Zahlenarray mit einigen ganzzahligen Werten. Anschließend wird die Funktion map(_:) für Zahlen aufgerufen und ihr Ergebnis dem Ergebnis zugewiesen.

Die Funktion map(_:) hat einen Parameter, einen Abschluss, der das Ergebnis $0 * $0 zurückgibt. Das $0 entspricht dem ersten Parameter des Abschlusses, also der Zahl aus den Zahlen, die transformiert wird.

Wir berechnen das Quadrat jeder Zahl in Zahlen. Im Wesentlichen wird die Operation $0 * $0 für jede Zahl in Zahlen aufgerufen und das resultierende Array dem Ergebnis zugewiesen. Sie transformieren – oder „zuordnen“ – ein Array in ein anderes.

Das Transformieren eines Arrays mit map(_:) ähnelt der Verwendung einer for-Schleife, ist jedoch viel prägnanter. So was:

seien Zahlen = [2, 3, 4, 5]
var Ergebnis = [Int]()

für Zahl in Zahlen {
Ergebnis += [number * number]
}

drucken(Ergebnis)

Hier ist eine andere Sichtweise. Mit map(_:) wird das eingegebene Zahlenarray in ein anderes Zahlenarray umgewandelt. So was:

2 => 2 * 2 => 4
3 => 3 * 3 => 9
4 => 4 * 4 => 16
5 => 5 * 5 => 25

Funktionen wie „map(_:)“ werden als Funktionen höherer Ordnung bezeichnet, da sie im Gegensatz zu gewöhnlichen Werten eine Funktion als Eingabe verwenden. Funktionen höherer Ordnung können auch Funktionen ausgeben, was für ein Programmierparadigma namens funktionale Programmierung nützlich ist.

Technisch gesehen können Sie Funktionen höherer Ordnung wie map(_:) für jede Sequenz aufrufen. Dazu gehören Sammlungen wie Arrays, Wörterbücher und Mengen, Bereiche wie 1…100 und sogenannte Iteratoren. Im Grunde alles, was wie eine „Liste“ von Werten aussieht.

Wir werden am Ende dieses Tutorials besprechen, warum Funktionen höherer Ordnung nützlich sind. Lassen Sie uns zunächst etwas über flatMap(_:) und compactMap(_:) lernen.

Die Funktion flatMap(_:) ähnelt map(_:), außer dass sie das resultierende Array „flacht“. Hier ist ein Beispiel:

seien Zahlen = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let result = zahlen.flatMap({ $0 })

drucken(Ergebnis)

Der obige Code beginnt mit einem verschachtelten Array von Ganzzahlen. Das Zahlenarray besteht aus einem Array von 3 Arrays, die jeweils 3 Zahlen enthalten.

Der Abschluss { $0 } gibt einfach das erste Argument des Abschlusses zurück, also die einzelnen verschachtelten Arrays. Es findet keine Transformation oder Operation statt. Wenn Sie jedoch flatMap(_:) für das Zahlenarray anstelle von map(_:) aufrufen, erhalten Sie ein abgeflachtes Array einzelner Zahlen. Im Gegensatz zu den Eingabe-Array-Nummern enthält das Ergebnis-Array keine verschachtelten Arrays!

Schauen wir uns ein anderes Beispiel an. Stellen Sie sich vor, Sie arbeiten mit vier Giraffengruppen und möchten eine einzelne Giraffengruppe erstellen, die größer als eine bestimmte Höhe ist. So machen Sie das:

lass Giraffen = [[5, 6, 9], [11, 2, 13, 20], [1, 13, 7, 8, 2]]
let tallest = giraffes.flatMap({ $0.filter({ $0 > 10 }) })

drucken (am höchsten)

Sehen Sie, wie Giraffen ein Array von Arrays enthalten? Im obigen Code wird die Funktion filter(_:) für jedes verschachtelte Array in Giraffen aufgerufen. Wir wollen nur Ganzzahlen (Giraffen!), die größer als 10 sind. Die resultierenden Arrays werden zu einem „flachen“ Array zusammengefasst und dem höchsten zugewiesen.

Überlegen Sie, was passieren würde, wenn wir map(_:) anstelle von flatMap(_:) verwendet hätten. Das resultierende Array würde nicht reduziert werden. Stattdessen wäre es so:

[[], [11, 13, 20], [13]]

Es ist wichtig zu beachten, dass die Funktion „flatMap(_:)“ zuerst „map(_:)“ für die Array-Elemente aufruft und diese dann reduziert. Deshalb funktioniert so etwas wie das Folgende nicht:

seien Zahlen = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let result = zahlen.flatMap({ $0 * 2 })

Im obigen Code bezieht sich $0 auf die Arrays innerhalb von Zahlen. Es ist unmöglich, ein Array mit zwei zu multiplizieren, deshalb funktioniert dieser Code nicht.

Es ist klug, sich Flatmapping so vorzustellen, als würde man ein Array in einer Dimension weniger sehen. Sie beginnen mit einem zweidimensionalen Array und erhalten nach flatMap(_:) ein eindimensionales Array.

Wie wäre es mit der Verwendung von flatMap(_:) mit Optionen? Schauen wir uns das als nächstes an.

Der Name „Compact Map“ basiert auf der Idee, dass das Entfernen von Nullelementen aus einem Array das Array kompakter macht. Ebenso kommt der Name „Flat Map“ von der Abflachung des Arrays. Und „Mapping“ ist ein Konzept aus der Mathematik, bei dem man die Werte in einer Menge mit einer anderen Menge verknüpft.

Die Funktion „compactMap(_:)“ entfernt Nullwerte aus dem Eingabearray. Es ist sehr nützlich, wenn Sie mit Optionen arbeiten.

Vor Swift 4.1 konnte die Funktion flatMap(_:) (oben) auch verwendet werden, um Nullwerte aus abgeflachten Arrays herauszufiltern. Seit Swift 4.1+ verwenden Sie zu diesem Zweck nun die explizite compactMap(_:).

Hier ist ein Beispiel:

seien Zahlen = [“5”, “42”, “nine”, “100”, “Bob”]
let result = zahlen.compactMap({ Int($0) })

drucken(Ergebnis)

Schau was passiert? Der wichtigste Teil des Codes ist Int($0). Dies nimmt eine einzelne Zeichenfolge aus Zahlen mit $0 und versucht, sie mit dem Int()-Initialisierer in eine Ganzzahl umzuwandeln.

Dieser Int()-Initialisierer ist fehlgeschlagen: Er kann nil (optional) zurückgeben, daher ist sein Rückgabetyp Int?. Daraus ergibt sich der Rückgabetyp der Mapping-Transformation [Int?] – ein Array optionaler Ganzzahlen.

[Optional(5), Optional(42), nil, Optional(100), nil]

Die Funktion „compactMap(_:)“ entfernt automatisch Nullelemente aus dem zurückgegebenen Array, nachdem sie „map(_:)“ dafür aufgerufen hat. Daher ist der Rückgabetyp nicht optional.

[5, 42, 100]

Im obigen Code lautet der Ergebnistyp [Int]. Wenn Sie map(_:) verwendet hätten, wäre der Rückgabetyp gewesen [Int?]. Und Sie hätten einen zusätzlichen Schritt benötigt, um die Werte aus dem Array auszupacken und mit ihnen zu arbeiten.

Bevor wir die realen Anwendungsfälle von Flatmap und Compactmap besprechen, werfen wir einen kurzen Rückblick auf diese Funktionen höherer Ordnung und ihre Zwecke.

  1. Die Funktion „map(_:)“ wendet einen Abschluss auf eine Eingabesammlung an und gibt die transformierte Sammlung zurück
  2. Die flatMap(_:)-Funktion macht dasselbe und reduziert außerdem die resultierende Sammlung
  3. Die Funktion „compactMap(_:)“ macht dasselbe wie „map(_:)“ und entfernt außerdem „nil“ aus der resultierenden Sammlung

Die abstrakte Arbeit mit Map(_:), FlatMap(_:) und CompactMap(_:) macht es manchmal schwer, sich ihre praktischen Anwendungsfälle vorzustellen. Lassen Sie uns besprechen, warum Sie sie verwenden möchten.

Die Verwendung von Funktionen wie map(_:) zum Anwenden von Transformationen auf Sequenzen hat einige Vorteile:

  • Dies ist prägnanter als die Verwendung einer for-Schleife, da Sie keine temporären Variablen und keinen mehrzeiligen for in { }-Block benötigen.
  • Normalerweise können Sie einen Aufruf von „map(_:)“ in eine Zeile schreiben, was Ihren Code (normalerweise) besser lesbar macht.
  • Funktionen wie „map(_:)“ können verkettet werden, sodass Sie nacheinander mehrere Transformationen auf eine Sequenz anwenden können.

Im Allgemeinen sind Funktionen höherer Ordnung nützlich, da Sie damit eine Funktion auf eine Folge von Werten anwenden können. Anstatt die Transformation prozedural zu codieren, können Sie einfach die Funktion anwenden und ein Ergebnis zurückerhalten.

Der praktischste Anwendungsfall für flatMap(_:) ist die Arbeit mit gruppierten oder verschachtelten Eingabewerten, der gewünschte Ausgabewert muss jedoch eindimensional sein.

In einer Musik-App könnten Sie beispielsweise drei Arrays haben: Songs, Künstler und Playlists. Sie kombinieren sie in einem Array, rufen darauf flatMap(_:) auf, wählen die Lieder, Künstler und Wiedergabelisten aus, für die isFavorite wahr ist, und erhalten am Ende eine flache Liste der Elemente, die als Favorit markiert wurden.

Ein praktischer Anwendungsfall für compactMap(_:) ist die Arbeit mit einer Transformation, die Null zurückgeben kann. Sie ersparen sich ein paar triviale Schritte, indem Sie CompactMap(_:) Nullwerte sofort herausfiltern lassen. Ein zusätzlicher Vorteil ist der nicht optionale Rückgabetyp von compactMap(_:). Im Vergleich dazu hätte die Funktion filter(_:) einen optionalen Wert zurückgegeben, wenn Sie Null herausgefiltert hätten.

Sie können flatMap(_:) und compactMap(_:) kombinieren und sogar filtern(_:) oder reduzieren(_:_:). Stellen Sie sich vor, Sie erstellen eine Social-Media-App. Sie möchten eine Zeitleiste mit Beiträgen für einen Benutzer erstellen. Sie verwenden drei Abfragen, um Beitrags-IDs für den Benutzer auszuwählen, beispielsweise aus Follower-Beiträgen, Anzeigen und Trendthemen.

  • Sie können map(_:) verwenden, um diese IDs in tatsächliche Post-Objekte zu erweitern
  • Sie können flatMap(_:) verwenden, um die drei Gruppen in einer Sammlung zusammenzufassen
  • Mit „compactMap(_:)“ können Sie Beiträge verwerfen, die nicht erweitert werden konnten

Weiterführende Literatur

Es lohnt sich, etwas über Map(_:), FlatMap(_:) und CompactMap(_:) zu lernen, da sie Ihren Code prägnanter und lesbarer machen. Sie können dem Code Ihrer App weitere funktionale Programmierung hinzufügen. Sobald Sie sich daran gewöhnt haben, können Sie nicht glauben, dass Sie Ihre Arbeit ohne sie erledigen könnten.

Besonders hervorzuheben sind die Unterschiede zwischen map(_:), flatMap(_:) und compactMap(_:). Der erste wendet eine Transformation auf eine Sequenz an, der zweite flacht das resultierende Array ab und der dritte entfernt Nullwerte, bevor er das Ergebnis zurückgibt. Eindrucksvoll!

Table of Contents