Как сделать дженерики в ограничении типа коллекции?

ios swift generics optional

359 просмотра

3 ответа

2611 Репутация автора

Я пытался извлечь ненулевые значения из массива String. Как ниже. Но мой старший хочет, чтобы он мог извлекать ненулевые значения из других типов.

Я прочитал, дженерики могут помочь мне для обработки различных типов. Как я могу использовать дженерики, чтобы я мог использовать следующее расширение как для работы с другими типами?

getNonNilдолжен возвращать извлеченные ненулевые значения определенного типа (т. е. если массив [String?], он должен возвращать [String], возвращает [Int], если [Int?] )

Потому что я должен делать дальнейшие расчеты.

То, что я попробовал, ниже:

import Foundation
// Extended the collection-type so that collectiontype is constrained to having element with optional strings
extension CollectionType where Self.Generator.Element == Optional<String>{
    func getNonNil() -> [String] {
        // filter out all nil elements and forcefully unwrap them using map
        return self.filter({$0 != nil}).map({$0!})
    }
}

// Usage
let x: [String?] = ["Er", "Err", nil, "errr"]

x.getNonNil().forEach { (str) in
    print(str)
}
Автор: Dari Источник Размещён: 18.07.2016 10:14

Ответы (3)


3 плюса

425078 Репутация автора

Чтобы getNonNilвы могли просто использовать

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

Для исходного вопроса обычно вы можете ввести протокол для типа Optional (например, через пакет muukii / OptionalProtocol ):

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] {
        ...
    }
}
Автор: kennytm Размещён: 18.07.2016 10:33

2 плюса

56333 Репутация автора

Нет простого способа добиться этого с помощью расширения, поскольку вы не можете вводить новые универсальные типы в расширения (хотя это является частью Манифеста Swift Generics - что вполне может быть в будущей версии Swift).

Как говорит @kennytm , самое простое решение - просто использовать flatMap, которое отфильтровывает nil:

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

Однако, если вы все еще хотите это как расширение, вы можете использовать обходной путь протокола, чтобы позволить ограничить расширение любым необязательным типом элемента (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)
}

(В Swift 3 CollectionTypeбыл переименован в Collectionи Generatorсейчас Iterator)

Хотя flatMapэто почти наверняка предпочтительнее в этой ситуации, я добавляю это только ради завершения.

Автор: Hamish Размещён: 18.07.2016 10:42

0 плюса

6112 Репутация автора

Самый простой подход заключается в использовании, flatMapкак предложил kennytm , но если вы абсолютно хотите знать, как создать такой метод с использованием обобщений, один из подходов заключается в создании глобального метода, который принимает коллекцию в качестве параметра:

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"]
Автор: xoudini Размещён: 18.07.2016 10:46
Вопросы из категории :
32x32