假设我有一个String类型的数组,我想把它映射到Int类型的数组我可以使用映射函数:
var arrayOfStrings: Array = ["0", "a"]
let numbersOptional = arrayOfStrings.map { $0.toInt() }
// numbersOptional = "[Optional(0), nil]"
Numbers现在是整型数组吗?,但我想要一个整型数组。我知道我可以这样做:
let numbers = arrayOfStrings.map { $0.toInt() }.filter { $0 != nil }.map { $0! }
// numbers = [0]
但这似乎不是很快。从Int数组转换?to Array of Int需要同时调用filter和map,其中包含很多样板文件。有更快捷的方法吗?
Update:在Swift 1.2中,有一个内置的数组flatMap
方法,但它不接受Optional
s,所以下面的帮助器仍然有用。
我喜欢使用辅助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
的定义是Optional
的一个特例,而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
构建新的数组可能更符合习惯
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
)方法。给定您需要应用的特定过滤器,在这种情况下,甚至flatMap
也可以工作(参见Jean-Philippe的答案)。可惜swift标准库没有提供这两个方法。
更新: Xcode 7.2•斯威夫特2.1.1
let arrayOfStrings = ["0", "a", "1"]
let numbersOnly = arrayOfStrings.flatMap { Int($0) }
print(numbersOnly) // [0,1]
没有好的内置Swift标准库方法来做到这一点。然而,Haskell有一个函数mapMaybe
,它可以做你正在寻找的(假设你只是想放弃nil值)。下面是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
}