Swift中flatMap和compactMap的区别

30

Swift 4.1中,flatMap似乎已经被弃用。但是,在Swift 4.1中有一个新方法compactMap可以完成相同的事情。 使用flatMap可以转换集合中的每个对象,然后删除任何为nil的项。
与flatMap类似

let array = ["1", "2", nil] 
array.flatMap { $0 } // will return "1", "2"

就像 compactMap 一样

let array = ["1", "2", nil] 
array.compactMap { $0 } // will return "1", "2"

compactMap 做的是同样的事情。

这两种方法有什么区别?为什么苹果决定重命名这个方法?

3个回答

28

Swift标准库为flatMap函数定义了3种重载:

Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element]  
Optional.flatMap<U>(_: (Wrapped) -> U?) -> U?  
Sequence.flatMap<U>(_: (Element) -> U?) -> [U]  

最后一个重载函数可能会被误用两种方式:
考虑以下结构体和数组:

struct Person {
  var age: Int
  var name: String
}  

let people = [
  Person(age: 21, name: "Osame"),
  Person(age: 17, name: "Masoud"),
  Person(age: 20, name: "Mehdi")
]

第一种方法:增加和删除封装:
如果您需要获取包含在people数组中的人的年龄数组,您可以使用两个函数:

let flatMappedAges = people.flatMap({$0.age})  // prints: [21, 17, 20]
let mappedAges = people.map({$0.age})  // prints: [21, 17, 20]  

在这种情况下,map函数将完成任务,不需要使用flatMap,因为两者产生相同的结果。此外,在使用flatMap的这种用例中存在无用的包装和解包过程。(闭包参数用Optional包装其返回值,而flatMap的实现在返回值之前会取消Optional的包装)

第二种方法 - 字符串符合集合协议:
假设你需要从people数组中获取人名列表。您可以使用以下代码:

let names = people.flatMap({$0.name})  

如果您使用的是4.0之前的Swift版本,则会获得一个转换后的列表

["Osame", "Masoud", "Mehdi"]  

但在更新的版本中,String符合Collection协议,因此,您对flatMap()的使用将匹配第一个重载函数而不是第三个,并为您转换后的值提供一个扁平化的结果:

["O", "s", "a", "m", "e", "M", "a", "s", "o", "u", "d", "M", "e", "h", "d", "i"]

那么,他们是如何解决这个问题的?他们弃用了flatMap()的第三个重载
由于这些误用,Swift团队决定弃用flatMap函数的第三个重载。到目前为止,他们解决需要处理Optional的情况的方法是引入一个名为compactMap()的新函数,该函数将给你所期望的结果。


1
很好的解释!顺便说一下,let mappedAges = people.map({$0.name}) 这行代码使用了 $0.name 而不是 $0.age - Toni Sučić

20

现有 flatMap 方法有三种不同的变体。接受返回 Optional 值的闭包的 Sequence.flatMap(_:) 变体已被弃用。在 Sequence 和 Optional 上的其他 flatMap(_:) 变体保持不变。根据提案文档中所述,原因是由于误用。

已弃用的 flatMap 变体的功能与新方法 compactMap 完全相同。

详情请见此处


2
@Krin-San,你误解了重点。flatMap有三种不同的变体。只有接受Optional Value的那个已经被弃用了。所以从你的例子中使用的flatMap是不同的方法,也没有被弃用。将你的分数更改为let scores:[[Int]?] = [[5,2,7], [4,8], [9,1,3]],你将看到弃用警告,并且与使用'compactMap'获得的相同结果。 - Bilal

0

高阶函数 - 是一种通过另一个函数作为参数或返回值来操作的函数。例如 - sort、map、filter、reduce...

map vs compactMap vs flatMap

[RxJava Map vs FlatMap]

  • map - 转换(Optional, Sequence, String)
  • flatMap - 将复杂的结构展平为单一结构(Optional, Collection)
  • compactMap - flatMap 的下一步。去除 nil

flatMap vs compactMap

在 Swift v4.1 之前,flatMap 有三种实现方式(没有 compactMap)。这些实现方式负责从序列中删除 nil。它们更多地涉及到 map 而不是 flatMap

实验

//---------------------------------------
//map - for Optional, Sequence, String, Combine
//transform

//Optional
let mapOptional1: Int? = Optional(1).map { $0 } //Optional(1)
let mapOptional2: Int? = Optional(nil).map { $0 } //nil
let mapOptional3: Int?? = Optional(1).map { _ in nil } //Optional(nil)
let mapOptional4: Int?? = Optional(1).map { _ in Optional(nil) } //Optional(nil)

//collection
let mapCollection1: [Int] = [1, 2].map { $0 } //[1, 2]
let mapCollection2: [Int?] = [1, 2, nil, 4].map { $0 } //Optional(1), Optional(2), nil, Optional(4),
let mapCollection3: [Int?] = ["Hello", "1"].map { Int($0) } //[nil, Optional(1)]

//String
let mapString1: [Character] = "Alex".map { $0 } //["A", "l", "e", "x"]




//---------------------------------------
//flatMap - Optional, Collection, Combime

//Optional
let flatMapOptional1: Int? = Optional(1).flatMap { $0 } //Optional(1)
let flatMapOptional2: Int? = Optional(nil).flatMap { $0 } //nil
let flatMapOptional3: Int? = Optional(1).flatMap { _ in nil }
let flatMapOptional4: Int? = Optional(1).flatMap { _ in Optional(nil) }

//Collection
let flatMapCollection1: [Int] = [[1, 2], [3, 4]].flatMap { $0 } //[1, 2, 3, 4]
let flatMapCollection2: [[Int]] = [[1, 2], nil, [3, 4]].flatMap { $0 } //DEPRECATED(use compactMap): [[1, 2], [3, 4]]
let flatMapCollection3: [Int?] = [[1, nil, 2], [3, 4]].flatMap { $0 } //[Optional(1), nil, Optional(2), Optional(3), Optional(4)]
let flatMapCollection4: [Int] = [1, 2].flatMap { $0 } //DEPRECATED(use compactMap):[1, 2]




//---------------------------------------
//compactMap(one of flatMap before 4.1) - Array, Combine
//removes nil from the input array

//Collection
let compactMapCollection1: [Int] = [1, 2, nil, 4].compactMap { $0 } //[1, 2, 4]
let compactMapCollection2: [[Int]] = [[1, 2], nil, [3, 4]].compactMap { $0 } //[[1, 2], [3, 4]]

[Swift 可选类型中的 map 和 flatMap]
[Swift 函子、适用函子和单子]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接