SwiftでArray#flattenする

  • 3
    Like
  • 0
    Comment
More than 1 year has passed since last update.

Array#flatten

以下のような実装をしてみました。

extension Array {
    func flatten() -> [AnyObject] {
        var flattenArray = [AnyObject]()
        for element in self {
            if let element = element as? NSArray as? [AnyObject] {
                flattenArray += element.flatten()
            } else {
                flattenArray.append(element as NSObject)
            }
        }
        return flattenArray
    }
}

var temp = [1, [2, 3, 4, [5, 6]]]
var flat = temp.flatten()
println(flat) // [1, 2, 3, 4, 5, 6]

実装について補足

if let element = element as NSArray as? [AnyObject] {}
という処理について少し補足します。

まず、なんでNSArrayにキャストしてからArray<AnyObject>にキャストしているんだ?
と疑問に思われるかもしれませんが、
これはT型(placeholder type)のelementを直接Array<AnyObject>へキャストしようとすると、elementの実際の型がArray<AnyObject>だったとしてもnilになるためです。

if let element = element as? [AnyObject] {}とした場合、この挙動は以下のような期待していない結果を招きます。

var temp:[Any] = [[1, [2, 3, 4, [5, 6]]]]
var flat = temp.flatten()
println(flat) // [(1,(2,3,4,(5,6)))]

つまりelementArray<AnyObject>にキャストすることに失敗し、再帰的に平坦化できなくなっていまっている、ということです。

解決策

そこで、Working with Cocoa Data Typesで説明されている、
「要素の型がクラスのインスタンスでないArrayNSArrayにキャストしたときに、Swiftは暗黙的に要素の型もAnyObject互換な型にキャストする」

let schoolSupplies: NSArray = ["Pencil", "Eraser", "Notebook"]
// schoolSupplies is an NSArray object containing NSString objects

という性質を利用して、elementを間接的にArray<AnyObjec>にキャストしています。