有没有办法测试数组的类型是否可选?

时间:2015-01-30 05:10:45

标签: swift

让我们说我想在Array上编写一个方法,如果数组的类型是非可选的,则返回数组的副本,如果类型是可选的,则返回未包装值的子数组。为此,我认为我需要能够测试数组的T类型是否是可选类型。例如,这只会返回数组的副本:

extension Array {        
    func unwrapped() -> Array<T> {
        return filter({
            var x: T? = $0
            return x != nil
        })
    }
}

据我所知,如果我知道我有一组可选项,我可以使用filter()和map():

let foo: [String?] = [nil, "bar", "baz"]
let bar: [String] = foo.filter({ $0 != nil }).map({ $0! })

我没有找到解决该特定问题的方法。相反,我想知道是否有一种方法可以在数组扩展中确定它的类型是否是可选的,这在许多不同的便利方法中都很有用。

4 个答案:

答案 0 :(得分:3)

一种可能的解决方案是使用全局函数而不是扩展;然后你可以用两个定义重载,一个用于非可选,一个用于可选类型:

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

unwrapped([1, 2, 3, 4, 5]) // -> [1, 2, 3, 4, 5]
unwrapped([1, 2, 3, nil, 5]) // -> [1, 2, 3, 5]

我不确定这是否保证能够正常工作;如果某人能够找到一个案例,或者在Swift指南中的某个地方发现它始终是正确的,那将会很有趣。

另见this question中的讨论。

答案 1 :(得分:0)

使用reduce:

let unwrapped = foo.reduce([String]()) {
    if let bar = $1 as String? { return $0 + [bar] }
    return $0
}

如果原件是非可选的,则返回副本;如果原件是可选的,则返回非零的未包装值的子数组。您不需要知道数组是否包含选项以执行此操作。

如果数组包含至少一个值,则可以确定该值是否为可选值,如下所示:

extension Array {
    func optTest() {
        switch reflect(self[0]).disposition {
            case .Optional:
                println("I am an optional")
            default:
                break
        }
    }
}

您只需要测试第一个值。但我还没弄明白如何测试一个空数组 - 我想你可以添加一个元素然后测试那个元素...

答案 2 :(得分:0)

Array结构可以扩展为返回其泛型类型,然后a protocol can be used to test for a typeless Optional

这个例子是针对Swift 2的,但我相信它在以前的版本中应该有类似的效果。

protocol OptionalProtocol {}

extension Optional : OptionalProtocol {}

extension Array {
    func elementType() -> Any.Type {
        return Element.self
    }
}

[String]().elementType() is OptionalProtocol.Type  // false
[String?]().elementType() is OptionalProtocol.Type // true

特定于Swift 2,Array扩展程序可以完全放弃,因为typealias允许访问Element类型:

protocol OptionalProtocol {}

extension Optional : OptionalProtocol {}

[String]().dynamicType.Element.self is OptionalProtocol.Type  // false
[String?]().dynamicType.Element.self is OptionalProtocol.Type // true

答案 3 :(得分:0)

  

为此,我认为我需要能够测试数组的类型T是否是可选类型。

不,你没必要 - 只需使用flatMap!

let foo: [String?] = [nil, "bar", "baz"]
let foo2: [String] = ["bar", "baz"]

foo.flatMap{$0}
foo2.flatMap{$0}
// yield both ["bar", "baz"] and are of type Array<String>