Swift: shortcut unwrapping of array of optionals

后端 未结 7 886
孤城傲影
孤城傲影 2020-12-03 00:54

Assume we have an array of optionals defined:

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

I

相关标签:
7条回答
  • 2020-12-03 01:14

    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! }
    
    0 讨论(0)
  • 2020-12-03 01:16

    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

    0 讨论(0)
  • 2020-12-03 01:16

    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]
    
    0 讨论(0)
  • 2020-12-03 01:19

    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.

    0 讨论(0)
  • 2020-12-03 01:24

    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", ""]
    
    0 讨论(0)
  • 2020-12-03 01:35

    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! }

    0 讨论(0)
提交回复
热议问题