51

Assume we have an array of optionals defined:

var arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]

I can force unwrap it in a short way: var arrayForCrash = arrayOfOptionals.map { $0! }

But that will make app to crash, is there any other short way(without explicitly unwrapping) how I can unwrap an array of optional?

ambientlight
  • 7,212
  • 3
  • 49
  • 61
  • @Antonio ["If you have a question that you already know the answer to, and you would like to document that knowledge in public so that others (including yourself) can find it later, it's perfectly okay to ask and answer your own question on a Stack Exchange site."](http://stackoverflow.com/help/self-answer) – Matt Gibson Aug 31 '14 at 08:43
  • removing my comments above - they are irrelevant now. @Hurden, I apologize again – Antonio Aug 31 '14 at 08:46

7 Answers7

143

This solution will get you a new array with all values unwrapped and all nil's filtered away.

Swift 4.1:

let arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]
let arrayWithNoOptionals = arrayOfOptionals.compactMap { $0 }

Swift 2.0:

let arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]
let arrayWithNoOptionals = arrayOfOptionals.flatMap { $0 }

Swift 1.0:

let arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]
let arrayWithNoOptionals = arrayOfOptionals.filter { $0 != nil }.map { $0! }
Cenny
  • 1,916
  • 1
  • 12
  • 19
9

Since it is an array of optionals, it is possible some of the entries are nil. Instead of force unwrapping with !, use the nil coalescing operator to turns nils into empty strings.

let arrayOfOptionals: [String?] = ["This", "array", nil, "has", "some", "nils", nil]

let array:[String] = arrayOfOptionals.map{ $0 ?? "" }
// array is now ["This", "array", "", "has", "some", "nils", ""]
vacawama
  • 150,663
  • 30
  • 266
  • 294
3

Although you can use flatMap { $0 } to remove nils, flatMap is actually a much more powerful function, and has an overloaded version which does something completely different (e.g. flatten [[Int]] to [Int]). If you're not careful, you may accidentally invoke the wrong function.

I would recommend using an extension on SequenceType to remove nils. If you use removeNils(), you'll be able to essentially do the following:

[1, nil, 2].removeNils() == [1, 2]

It works by making Optional conform to an OptionalType protocol which allows extending SequenceTypes that contain Optional values.

For more information see the original answer I posted.

Community
  • 1
  • 1
Senseful
  • 86,719
  • 67
  • 308
  • 465
2

Swift 4

Easy to read and safe approach to filter nils of any sequence

protocol OptionalProtocol {
    associatedtype Wrapped
    var optional: Wrapped? { get }
}

extension Optional: OptionalProtocol {
    var optional: Wrapped? {
        return self
    }
}

extension Sequence where Element: OptionalProtocol {
    var removingOptionals: [Element.Wrapped] {
        return self.compactMap { $0.optional }
    }
}

Usage

let array: [Int?] = [1, 2, nil, 3, 4, nil]
print(array.removingOptionals) // prints [1, 2, 3, 4], has type [Int]
Vadim Ahmerov
  • 708
  • 9
  • 12
1

I took @Cenny's answer and decided to make an operator out of it:

prefix operator <!> {}

prefix func <!> <T>(array: [T?]) -> [T] {
  return array.filter{ $0 != nil }.map{ $0! }
}

I'm using it to parse an array of JSON objects and filter the ones that failed:

static func parse(j: JSONArray) -> [Agency]? {
  return <!>j.map { self.parse($0) }
}

Update for Swift 2+:
Use flatMap operator and it'll only return non-nil objects

Yariv Nissim
  • 13,273
  • 1
  • 38
  • 44
1

The more interesting, how to unwrap an optional array of optional values. It is important to deal when we are working with JSON, because JSON can potentially contain null value for an array of something.

Example:

{ "list": null }
// or
{ "list": [null, "hello"] }

To fill a Swift struct we may have a model:

struct MyStruct: Codable {
    var list: [String?]?
}

And to avoid working with String?? as a first item we could:

var myStruct = try! JSONDecoder.init().decode(MyStruct.self, from: json.data(using: .utf8)!)
let firstItem: String? = s1.list?.compactMap { $0 }.first

With compactMap { $0 } we can avoid

let i2: String?? = s1.list?.first

compactMap { $0 } is an equivalent of filter { $0 != nil }. map { $0! }

ultraon
  • 2,220
  • 2
  • 28
  • 27
0

How about:

import Foundation

var test: [String!] = ["this","is","a",nil,"test"]
for string in test {
    if string != nil {
        print(string)
    }
}

Output is thisisatest.


In your case use [String!], if I understood you correctly.

Binarian
  • 12,296
  • 8
  • 53
  • 84