使用扩展方法递归映射数组
Recursive map over array using extension method
我从 API 收到了 collection 的 LayoutElement
。
为简单起见,假设它包含 2 个属性 -
String
或 [LayoutElement]
我想遍历此 collection,如果该值具有 children,则递归循环 children,返回包含 LayoutElement
的 LayoutElement
=12=]
基本上我应该得到一个 [LayoutElement]
,它只包含具有 String
值的元素。
因为这将在多个地方使用,我想我可以在 Array
上创建一个扩展,其中元素是 LayoutElement
但我不确定在 LayoutElement
的情况下如何最好地做到这一点=20=].
import UIKit
struct LayoutElement {
let children: [LayoutElement]?
let text: String?
}
let data = [
LayoutElement(children: nil, text: "First Text Element"),
LayoutElement(children: [
LayoutElement(children: nil, text: "Second Text Element"),
LayoutElement(children: nil, text: "Third Text Element"),
LayoutElement(children: [
LayoutElement(children: nil, text: "Fourth Text Element"),
], text: nil)
], text: nil),
]
extension Array where Element == LayoutElement {
var asLayout: [LayoutElement] {
return map { element in
if let children = element.children {
// ???
} else {
return element
}
}
}
}
let output = data.asLayout
print(output)
您应该能够通过在返回结果之前在本地数组中捕获您的集合来实现这一点
extension Array where Element == LayoutElement {
var asLayout: [LayoutElement] {
var flatten: [LayoutElement] = []
forEach { item in
if let children = item.children {
children.asLayout.forEach { child in
flatten.append(child)
}
} else {
flatten.append(item)
}
}
return flatten
}
}
有一个临时数组来收集所有结果:
extension Array where Element == LayoutElement {
var asLayout: [LayoutElement] {
var result = [LayoutElement]()
forEach {
if let children = [=10=].children {
result.append(contentsOf: children.asLayout)
} else if [=10=].text != nil {
result.append([=10=])
}
}
return result
}
}
您不知道该怎么做,因为您选择了错误的高阶函数。您应该使用 flatMap
,而不是 map
。
map
将每个数组元素转换为另一个元素,但 flatMap
可以将每个元素转换为 多个 其他元素。当结果数组的大小与起始数组的大小不同时,您应该使用 flatMap
,这里就是这种情况。 data
有 2 个元素,而 output
应该有 4 个元素。 map
永远不能增加元素的数量。
var asLayout: [LayoutElement] {
return flatMap { element -> [LayoutElement] in
// for each element, we return the array of elements into which we want to transform it
if let children = element.children {
return children.asLayout
} else {
return [element] // we don't want to transform it, so we return an array containing only "element"
}
}
}
返回 children.asLayout
就可以了。
或一行:
var asLayout: [LayoutElement] {
flatMap { [=11=].children?.asLayout ?? [[=11=]] }
}
我从 API 收到了 collection 的 LayoutElement
。
为简单起见,假设它包含 2 个属性 -
String
或 [LayoutElement]
我想遍历此 collection,如果该值具有 children,则递归循环 children,返回包含 LayoutElement
的 LayoutElement
=12=]
基本上我应该得到一个 [LayoutElement]
,它只包含具有 String
值的元素。
因为这将在多个地方使用,我想我可以在 Array
上创建一个扩展,其中元素是 LayoutElement
但我不确定在 LayoutElement
的情况下如何最好地做到这一点=20=].
import UIKit
struct LayoutElement {
let children: [LayoutElement]?
let text: String?
}
let data = [
LayoutElement(children: nil, text: "First Text Element"),
LayoutElement(children: [
LayoutElement(children: nil, text: "Second Text Element"),
LayoutElement(children: nil, text: "Third Text Element"),
LayoutElement(children: [
LayoutElement(children: nil, text: "Fourth Text Element"),
], text: nil)
], text: nil),
]
extension Array where Element == LayoutElement {
var asLayout: [LayoutElement] {
return map { element in
if let children = element.children {
// ???
} else {
return element
}
}
}
}
let output = data.asLayout
print(output)
您应该能够通过在返回结果之前在本地数组中捕获您的集合来实现这一点
extension Array where Element == LayoutElement {
var asLayout: [LayoutElement] {
var flatten: [LayoutElement] = []
forEach { item in
if let children = item.children {
children.asLayout.forEach { child in
flatten.append(child)
}
} else {
flatten.append(item)
}
}
return flatten
}
}
有一个临时数组来收集所有结果:
extension Array where Element == LayoutElement {
var asLayout: [LayoutElement] {
var result = [LayoutElement]()
forEach {
if let children = [=10=].children {
result.append(contentsOf: children.asLayout)
} else if [=10=].text != nil {
result.append([=10=])
}
}
return result
}
}
您不知道该怎么做,因为您选择了错误的高阶函数。您应该使用 flatMap
,而不是 map
。
map
将每个数组元素转换为另一个元素,但 flatMap
可以将每个元素转换为 多个 其他元素。当结果数组的大小与起始数组的大小不同时,您应该使用 flatMap
,这里就是这种情况。 data
有 2 个元素,而 output
应该有 4 个元素。 map
永远不能增加元素的数量。
var asLayout: [LayoutElement] {
return flatMap { element -> [LayoutElement] in
// for each element, we return the array of elements into which we want to transform it
if let children = element.children {
return children.asLayout
} else {
return [element] // we don't want to transform it, so we return an array containing only "element"
}
}
}
返回 children.asLayout
就可以了。
或一行:
var asLayout: [LayoutElement] {
flatMap { [=11=].children?.asLayout ?? [[=11=]] }
}