Blog

Die Macht von map und flatMap der Swift-Optionals

Lammert Westerhoff

Aktualisiert Oktober 22, 2025
7 Minuten

Bis vor kurzem hatte ich immer das Gefühl, dass mir in Swift etwas fehlt. Etwas, das die Arbeit mit Optionals viel einfacher macht. Und erst vor kurzem habe ich herausgefunden, dass das, was ich vermisst habe, bereits existiert. Ich spreche von den map- und flatMap-Funktionen der Swift-Optionals (nicht von der Array map-Funktion ). Vielleicht liegt es daran, dass sie in den Abschnitten über optionals im Swift-Handbuch nicht erwähnt werden und dass ich sie in keinem anderen Beispiel oder Tutorial gesehen habe. Und nachdem ich mich umgehört hatte, fand ich heraus, dass einige meiner Swift-Programmiererkollegen ebenfalls nichts davon wussten. Da ich sie für eine erstaunliche Swift-Funktion halte, die Ihren Swift-Code oft viel eleganter macht, möchte ich meine Erfahrungen damit mit Ihnen teilen.Wenn Sie die Funktionen map und flatMap ebenfalls noch nicht kannten, sollten Sie weiterlesen. Wenn Sie sie bereits kannten, hoffe ich, Ihnen einige gute, reale und nützliche Beispiele für ihre Verwendung zu zeigen, an die Sie vielleicht noch nicht gedacht haben.

Was bewirken map und flatMap?

Lassen Sie mich Ihnen zunächst ein kurzes Beispiel dafür geben, was die Funktionen tun. Wenn Sie damit bereits vertraut sind, können Sie die Beispiele überspringen. Die Funktion map wandelt einen optionalen Typ in einen anderen Typ um, falls er nicht nil ist, und gibt ansonsten einfach nil zurück. Dazu nimmt sie eine Schließung als Parameter an. Hier ist ein sehr einfaches Beispiel, das Sie in einem Swift Playground ausprobieren können: [code language="obj-c"] var value: Int? = 2 var newValue = value.map { $0 2 } // newValue ist jetzt Optional(4) value = nil newValue = value.map { $0 2 } // newValue ist jetzt nil [/code] Auf den ersten Blick mag dies seltsam erscheinen, denn wir rufen eine Funktion mit einem optionalen Element auf. Und müssen wir diese nicht immer erst entpacken? In diesem Fall nicht. Das liegt daran, dass die map-Funktion eine Funktion des Typs Optional ist und nicht des Typs, der durch das Optional umhüllt wird.Die flatMap ist so ziemlich das Gleiche wie map, außer dass der Rückgabewert der Closure in map nicht Null zurückgeben darf, während die Closure von flatMap Null zurückgeben kann. Sehen wir uns ein weiteres grundlegendes Beispiel an: [code language="obj-c"] var value: Double? = 10 var newValue: Double? = value.flatMap { v in if v < 5.0 { return nil } return v / 5.0 } // newValue ist jetzt Optional(2) newValue = newValue.flatMap { v in if v < 5.0 { return nil } return v / 5.0 } // jetzt ist es nil [/code] Wenn wir in diesem Fall versuchen würden, map statt flatMap zu verwenden, würde es sich nicht kompilieren lassen.

Wann sollten Sie es verwenden?

In vielen Fällen, in denen Sie einen ternären Operator verwenden, um zu prüfen, ob ein optionaler Wert nicht null ist, und dann einen Wert zurückgeben, wenn er nicht null ist, und andernfalls null zurückgeben, ist es wahrscheinlich besser, eine der map-Funktionen zu verwenden. Wenn Sie das folgende Muster erkennen, sollten Sie Ihren Code durchgehen und einige Änderungen vornehmen: [code language="obj-c"] var value: Int? = 10 var newValue: Int? = value != nil ? value! + 10 : nil // oder andersherum: var otherValue: Int? = wert == nil ? nil : wert! + 10 [/code] Das Force Unwrapping sollte bereits darauf hinweisen, dass etwas nicht ganz richtig ist. Verwenden Sie daher stattdessen die zuvor gezeigte map-Funktion. Um das Force Unwrapping zu vermeiden, hätten Sie stattdessen eine einfache if let- oder guard-Anweisung verwenden können: [code language="obj-c"] func addTen(value: Int?) -> Int? { if let value = value { return value + 10 } return nil } func addTwenty(value: Int?) -> Int? { guard let value = value else { return nil } return value + 20 } [/code] Dies tut immer noch genau dasselbe wie der ternäre Operator und ist daher besser mit einer map-Funktion geschrieben.

Nützliche reale Beispiele für die Verwendung der Kartenfunktionen

Sehen wir uns nun einige Beispiele an, in denen Sie die Map-Funktionen auf intelligente Weise verwenden können, an die Sie vielleicht nicht sofort denken. Am meisten profitieren Sie davon, wenn Sie sofort eine bestehende Funktion übergeben können, die den Typ, der von der optionalen Funktion umschlossen wird, als einzigen Parameter übernimmt. In den folgenden Beispielen zeige ich die Funktion zunächst ohne Map-Funktion und dann noch einmal mit einer Map-Funktion umgeschrieben.

Datum formatieren

Ohne Karte: [code language="obj-c"] var date: NSDate? = ... var formatted: String? = date == nil ? nil : NSDateFormatter().stringFromDate(date!) [/code] Mit Karte: [code language="obj-c"] var date: NSDate? = ... var formatted: date.map(NSDateFormatter().stringFromDate) [/code]

Segue von Zelle in UITableView

Ohne Karte: [code language="obj-c"] func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if let cell = sender as? UITableViewCell, let indexPath = tableView.indexPathForCell(cell) { (segue.destinationViewController as! MyViewController).item = items[indexPath.row] } } [/code] Mit Karte: [code language="obj-c"] func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if let indexPath = (sender as? UITableViewCell).flatMap(tableView.indexPathForCell) { (segue.destinationViewController as! MyViewController).item = items[indexPath.row] } } [/code]

Werte in String-Literalen

Ohne Karte: [code language="obj-c"] func ageToString(age: Int?) -> String { return age == nil ? "Unbekanntes Alter" : "Sie ist (Alter!) Jahre alt" } [/code] Mit Karte: [code language="obj-c"] func ageToString(age: Int?) -> String { return age.map { "Sie ist ($0) Jahre alt" } ?? "Unbekanntes Alter" } [/code] (Bitte beachten Sie, dass in den obigen Beispielen Backslashes vor (age!) und ($0) stehen müssen, aber leider :-( bricht das die Formatierung von Wordpress in diesem Beitrag)

Lokalisierte Zeichenfolgen

Ohne Karte: [code language="obj-c"] let label = UILabel() func updateLabel(value: String?) { if let value = value { label.text = String.localizedStringWithFormat( NSLocalizedString("value %@", comment: ""), value) } else { label.text = nil } } [/code] Mit Karte: [code language="obj-c"] let label = UILabel() func updateLabel(value: String?) { label.text = value.map { String.localizedStringWithFormat(NSLocalizedString("value %@", comment: ""), $0) } } [/code]

Enum mit rawValue von optional mit Standard

Ohne Karte: [code language="obj-c"] enum State: String { case Default = "" case Cancelled = "CANCELLED" static func parseState(state: String?) -> State { guard let state = state else { return .Default } return State(rawValue: state) ?? .Default } } [/code] Mit Karte: [code language="obj-c"] enum State: String { case Default = "" case Cancelled = "CANCELLED" static func parseState(state: String?) -> State { return state.flatMap(State.init) ?? .Default } } [/code]

Element in Array suchen

Mit Artikeln wie: [code language="obj-c"] struct Item { let identifier: String let value: String } let items: [Item] [/code] Ohne Karte: [code language="obj-c"] func find(identifier: String) -> Item? { if let index = items.indexOf({$0.identifier == identifier}) { return items[index] } return nil } [/code] Mit Karte: [code language="obj-c"] func find(identifier: String) -> Item? { return items.indexOf({$0.identifier == identifier}).map({items[$0]}) } [/code]

Objekte mit json wie Dictionaries konstruieren

Mit einer Struktur (oder Klasse) wie: [code language="obj-c"] struct Person { let firstName: String let lastName: String init?(json: [String: AnyObject]) { if let firstName = json["firstName"] as? String, let lastName = json["lastName"] as? String { self.firstName = firstName self.lastName = lastName return } return nil } } [/code] Ohne Karte: [code language="obj-c"] func createPerson(json: [String: AnyObject]) -> Person? { if let personJson = json["person"] as? [String: AnyObject] { return Person(json: personJson) } return nil } [/code] Mit Karte: [code language="obj-c"] func createPerson(json: [String: AnyObject]) -> Person? { return (json["Person"] as? [String: AnyObject]).flatMap(Person.init) } [/code]

Konslusion

Die Funktionen map und flatMap können unglaublich leistungsfähig sein und Ihren Code eleganter machen. Ich hoffe, dass Sie anhand dieser Beispiele erkennen können, in welchen Situationen es für Ihren Code wirklich von Vorteil ist, wenn Sie diese Funktionen verwenden. Bitte lassen Sie mich in den Kommentaren wissen, wenn Sie ähnliche intelligente Beispiele für die Verwendung von map und flatMap kennen, und ich werde sie der Liste hinzufügen.

Verfasst von

Lammert Westerhoff

Contact

Let’s discuss how we can support your journey.