如何在Swift中打开数组的元素? (即Array <Int?> as Array <Int>)

比方说,我有一个string数组,我想将其映射到一个Int数组我可以使用映射函数:

var arrayOfStrings: Array = ["0", "a"] let numbersOptional = arrayOfStrings.map { $0.toInt() } // numbersOptional = "[Optional(0), nil]" 

Numbers现在是一个Int的数组,但我想要一个Int数组。 我知道我可以做到这一点:

 let numbers = arrayOfStrings.map { $0.toInt() }.filter { $0 != nil }.map { $0! } // numbers = [0] 

但是这似乎不是很快。 从Int数组转换? 数组的Int要求调用filter和地图几乎相同的样板材料。 有没有更快捷的方法来做到这一点?

更新: Xcode 7.2•Swift 2.1.1

 let arrayOfStrings = ["0", "a", "1"] let numbersOnly = arrayOfStrings.flatMap { Int($0) } print(numbersOnly) // [0,1] 

更新:从Swift 1.2开始,数组有一个内置的flatMap方法,但是它不接受Optional ,所以下面的helper仍然有用。


我喜欢使用帮助器flatMap函数来处理这些事情,就像Scala对集合的flatMap方法(可以将Scala Option视为0或1元素的集合,粗略地讲):

 func flatMap<C : CollectionType, T>(source: C, transform: (C.Generator.Element) -> T?) -> [T] { var buffer = [T]() for elem in source { if let mappedElem = transform(elem) { buffer.append(mappedElem) } } return buffer } let a = ["0", "a", "42"] let b0 = map(a, { $0.toInt() }) // [Int?] - [{Some 0}, nil, {Some 42}] let b1 = flatMap(a, { $0.toInt() }) // [Int] - [0, 42] 

flatMap这个定义对于更一般的flatMap应该做什么来说是一个特殊情况:

 func flatMap<C : CollectionType, T : CollectionType>(source: C, transform: (C.Generator.Element) -> T) -> [T.Generator.Element] { var buffer = [T.Generator.Element]() for elem in source { buffer.extend(transform(elem)) } return buffer } 

我们会得到的

 let b2 = flatMap(a, { [$0, $0, $0] }) // [String] - ["0", "0", "0", "a", "a", "a", "42", "42", "42"] 

使用reduce来构build新的数组可能更习惯于使用

 func filterInt(a: Array<String>) -> Array<Int> { return a.reduce(Array<Int>()) { var a = $0 if let x = $1.toInt() { a.append(x) } return a } } 

 filterInt(["0", "a", "42"]) // [0, 42] 

你真正想要的是一个collectmap + filter )方法。 鉴于你需要应用的具体filter,在这种情况下,即使flatMap也可以工作(请参阅Jean-Philippe的回答)。 太糟糕了,这两种方法都不是由swift标准库提供的。

有没有好的内置Swift标准库的方式来做到这一点。 然而,Haskell有一个函数mapMaybe ,它可以完成你正在寻找的任务(假设你只是想要消除零值)。 这是Swift中的一个等价物:

 func mapSome<S: SequenceType, D: ExtensibleCollectionType> (source: S, transform: (S.Generator.Element)->D.Generator.Element?) -> D { var result = D() for x in source { if let y = transform(x) { result.append(y) } } return result } // version that defaults to returning an array if unspecified func mapSome<S: SequenceType, T> (source: S, transform: (S.Generator.Element)->T?) -> [T] { return mapSome(source, transform) } let s = ["1","2","elephant"] mapSome(s) { $0.toInt() } // [1,2] 

你可以考虑使用reduce ,它更加灵活:

 var arrayOfStrings: Array = ["0", "a"] let numbersOptional = arrayOfStrings.reduce([Int]()) { acc, str in if let i = str.toInt() { return acc + [i] } return acc }