如何在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]
你真正想要的是一个collect
( map
+ 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 }