compactMap结果很奇怪

时间:2018-06-06 12:07:10

标签: swift

我希望allMembers属于[Member]类型。但它的类型是[[Member]]。为什么compactMap不会返回[Member]类型的结果?

class Team {
    let members = Array(repeating: Member(), count: 2)
}

class Member {
}

let teams = Array(repeating: Team(), count: 3)
let allMembers = teams.compactMap { $0.members }

2 个答案:

答案 0 :(得分:3)

您实际需要flatMap,而不是compactMap

尽管之前(在Swift 4.1之前),compactMap也称为flatMap,但它具有与当前flatMap不同的实现和函数签名,因为compactMap可以使用mapfilter调用map每个元素到新元素,而只保留non-nil个元素。另一方面,flatMap在映射元素时展平嵌套列表。

ThisflatMap上仍然存在的SequencethisflatMap上已弃用的Sequence,已重命名为compactMap 1}}。如您所见,重命名版本的函数签名是

func flatMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

,所以它的闭包输入参数返回Optional值(就像compactMap现在一样),而现有的flatMap有一个函数签名

func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence

,在其闭包中不返回Optional

您应该使用未弃用的flatMap来展平您的嵌套Array<Array<Member>>

let allMembers = teams.flatMap { $0.members }

答案 1 :(得分:0)

在@ David的回答中添加一些细节:

  

在序列(如数组)上使用flatMap,过滤任何映射到nil的内容现已弃用,并替换为compactMap。

在带有闭包的序列上使用flatMap,该闭包返回一个可选的。

Sequence.flatMap<U>(_ transform: (Element) -> U?) -> U?

例如:

而不是:

let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let valid = names.flatMap { $0 }

你必须使用:

let valid = names.compactMap { $0 }
// ["Tom", "Peter", "Harry"]
let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let counts = names.compactMap { $0?.count }
// [3, 5, 5]
  

但同样,Swift 4.1并没有弃用flatMap的所有用途 - 在3个案例中,只有一个案例正在发生变化。以下两种情况就是您需要使用flatMap的地方:

1)在具有返回序列的闭包的序列上使用flatMap:

Sequence.flatMap<S>(_ transform: (Element) -> S) -> [S.Element] where S : Sequence

示例:

let scores = [[5,2,7], [4,8], [9,1,3]]
let allScores = scores.flatMap { $0 }
// [5, 2, 7, 4, 8, 9, 1, 3]

let passMarks = scores.flatMap { $0.filter { $0 > 5} }
// [7, 8, 9]

2)在可选项上使用flatMap:闭包采用optional的非nil值并返回一个可选项。如果原始可选项为nil,则flatMap返回nil:

Optional.flatMap<U>(_ transform: (Wrapped) -> U?) -> U?

示例:

let input: Int? = Int("8")
let passMark: Int? = input.flatMap { $0 > 5 ? $0 : nil}
// Optional(8)

有关详细信息,请参阅here