获取未知类型的Array的元素和数量

时间:2016-04-04 19:21:30

标签: swift swift2.2

假设我们有一个数组,分配给类型为Any

的变量
let something: Any = ["one", "two", "three"]

我们也假设我们不知道它是一个数组还是其他东西。而且我们也不知道我们正在处理什么样的Array.Element

现在我们想知道它是否是一个数组。

let isArray = something is Array // compiler error
let isArray = (something as? [Any?] != nil) // does not work (array is [String] and not [Any?])

是否有任何优雅的解决方案可以从swift类型系统中获取以下信息:

  1. 给定对象是否为数组
  2. 数组的数量是什么
  3. 给我数组的元素
  4. (桥接到NSArray不是我的解决方案,因为我的数组也可以是[Any?]类型并且包含nil-values)

5 个答案:

答案 0 :(得分:4)

我喜欢@ stefreak的问题和他的解决方案。请记住@dfri关于Swift运行时内省的优秀答案,但是,我们可以在某种程度上简化和概括@ stefreak的“类型标记”方法:

protocol AnySequenceType {
    var anyElements: [Any?] { get }
}

extension AnySequenceType where Self : SequenceType {
    var anyElements: [Any?] {
        return map{
            $0 is NilLiteralConvertible ? Mirror(reflecting: $0).children.first?.value : $0
        }
    }
}

extension Array : AnySequenceType {}
extension Set   : AnySequenceType {}
//    ... Dictionary, etc.

使用:

let things:  Any = [1, 2]
let maybies: Any = [1, nil] as [Int?]

(things  as? AnySequenceType)?.anyElements // [{Some 1}, {Some 2}]
(maybies as? AnySequenceType)?.anyElements // [{Some 1}, nil]

请参阅Swift Evolution mailing list discussion关于允许协议扩展的可能性:

extension<T> Sequence where Element == T?

然而,在目前的实践中,更常见和有些虎头蛇尾的解决方案是:

things as? AnyObject as? [AnyObject] // [1, 2]

// ... which at present (Swift 2.2) passes through `NSArray`, i.e. as if we:

import Foundation
things as? NSArray  // [1, 2]

// ... which is also why this fails for `mabyies`
maybies as? NSArray // nil

无论如何,所有这一切让我感到振奋的是,一旦你丢失了类型信息,就不会再回头了。即使你反思Mirror,你仍然会得到一个dynamicType,你必须切换到一个期望的类型,这样你就可以转换它并使用它...所有在运行时,所有永远在编译时间之外检查和理智。

答案 1 :(得分:3)

作为@milos和OP:s协议一致性检查的替代方法,我将使用somethingfoobar的运行时内省添加一个方法,如下例所示)

/* returns an array if argument is an array, otherwise, nil */
func getAsCleanArray(something: Any) -> [Any]? {
    let mirr = Mirror(reflecting: something)
    var somethingAsArray : [Any] = []
    guard let disp = mirr.displayStyle where disp == .Collection else {
        return nil // not array
    }

    /* OK, is array: add element into a mutable that
     the compiler actually treats as an array */
    for (_, val) in Mirror(reflecting: something).children {
        somethingAsArray.append(val)
    }

    return somethingAsArray
}

使用示例:

/* example usage */
let foo: Any = ["one", 2, "three"]
let bar: [Any?] = ["one", 2, "three", nil, "five"]

if let foobar = getAsCleanArray(foo) {
    print("Count: \(foobar.count)\n--------")
    foobar.forEach { print($0) }
} /* Count: 3
     --------
     one
     2
     three      */

if let foobar = getAsCleanArray(bar) {
    print("Count: \(foobar.count)\n-------------")
    foobar.forEach { print($0) }
} /* Count: 5
     -------------
     Optional("one")
     Optional(2)
     Optional("three")
     nil
     Optional("five")  */

答案 2 :(得分:1)

我想出的唯一解决方案如下,但我不知道它是否是最优雅的解决方案:)

protocol AnyOptional {
    var anyOptionalValue: Optional<Any> { get }
}
extension Optional: AnyOptional {
    var anyOptionalValue: Optional<Any> {
        return self
    }
}
protocol AnyArray {
    var count: Int { get }
    var allElementsAsOptional: [Any?] { get }
}
extension Array: AnyArray {
    var allElementsAsOptional: [Any?] {
        return self.map {
            if let optional = $0 as? AnyOptional {
                return optional.anyOptionalValue
            }
            return $0 as Any?
        }
    }
}

现在你可以说

if let array = something as? AnyArray {
    print(array.count)
    print(array.allElementsAsOptional)
}

答案 3 :(得分:0)

这适合我在操场上玩:

// Generate fake data of random stuff
let array: [Any?] = ["one", "two", "three", nil, 1]
// Cast to Any to simulate unknown object received
let something: Any = array as Any

// Use if let to see if we can cast that object into an array
if let newArray = something as? [Any?] {
    // You now know that newArray is your received object cast as an
    // array and can get the count or the elements
} else {
    // Your object is not an array, handle however you need.
}

答案 4 :(得分:0)

我发现转换为AnyObject适用于一组对象。仍在研究价值类型的解决方案。

let something: Any = ["one", "two", "three"]

if let aThing = something as? [Any] {
    print(aThing.dynamicType) // doesn't enter
}

if let aThing = something as? AnyObject {
    if let theThing = aThing as? [AnyObject] {
        print(theThing.dynamicType) // Array<AnyObject>
    }
}