How to make generics in collection type constraint?

后端 未结 3 967
执笔经年
执笔经年 2020-12-04 03:04

I have been trying to extract non-nil values from the String array. Like below. But, my senior wants it to be able to extract non-nil values from other type

相关标签:
3条回答
  • 2020-12-04 03:20

    The easiest approach is using flatMap as kennytm suggested, but if you absolutely want to know how to create such a method using generics, one approach would be to create a global method that takes in the collection as a parameter:

    public func getNonNil<T, C: CollectionType where C.Generator.Element == Optional<T>>(collection: C) -> [T] {
        return collection.filter({$0 != nil}).map({$0!})
    }
    
    let x: [String?] = ["Er", "Err", nil, "errr"]
    
    print(getNonNil(x)) // returns ["Er", "Err", "errr"]
    
    0 讨论(0)
  • 2020-12-04 03:24

    For getNonNil you could simply use

    x.flatMap { $0 }
    // returns ["Er", "Err", "errr"] which is [String]
    

    For the original question, typically you could introduce a protocol to the Optional type (e.g. via the muukii/OptionalProtocol package):

    protocol OptionalProtocol {
        associatedtype Wrapped
        var value: Wrapped? { get }
    }
    
    extension Optional: OptionalProtocol {
        public var value: Wrapped? { return self }
    }
    
    extension CollectionType where Self.Generator.Element: OptionalProtocol {
        func getNonNil() -> [Self.Generator.Element.Wrapped] {
            ...
        }
    }
    
    0 讨论(0)
  • 2020-12-04 03:31

    There's no easy way of achieving this through an extension, as you cannot introduce new generic types into extensions (although this is part of the Swift Generics Manifesto – so may well be possibly in a future version of Swift).

    As @kennytm says, the simplest solution is just to use flatMap, which filters out nil:

    x.flatMap{$0}.forEach { (str) in
        print(str)
    }
    

    If however, you still want this as an extension, you could use a protocol workaround in order to allow you to constrain the extension to any optional element type (Swift 3):

    protocol _OptionalProtocol {
        associatedtype Wrapped
        func _asOptional() -> Wrapped?
    }
    
    extension Optional : _OptionalProtocol {
        func _asOptional() -> Wrapped? {return self}
    }
    
    extension Collection where Self.Iterator.Element : _OptionalProtocol {
        func getNonNil() -> [Iterator.Element.Wrapped] {
            return flatMap{$0._asOptional()}
        }
    } 
    
    ...
    
    let x : [String?] = ["Er", "Err", nil, "errr"]
    
    x.getNonNil().forEach { (str) in
        print(str)
    }
    

    (In Swift 3, CollectionType has been renamed to Collection, and Generator is now Iterator)

    Although flatMap is almost certainly preferred in this situation, I'm only really adding this for the sake of completion.

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