我有一组客户对象,每个客户对象都有自己的ID和父ID。
示例:
[
Customer(id: 1, parentId: nil),
Customer(id: 2, parentId: 3),
Customer(id: 3, parentId: 1),
Customer(id: 4, parentId: 1)
]
我的目标是重新排列它们,以便所有的孩子都在阵列中的父母之下,就像这样(或类似的东西):
[
Customer(id: 1, parentId: nil),
Customer(id: 4, parentId: 1),
Customer(id: 3, parentId: 1),
Customer(id: 2, parentId: 3)
]
然后,我将使用它来缩进表格视图单元格以显示客户的层次结构。
答案 0 :(得分:1)
您可以使用递归来实现这种嵌套的分层排序,例如
/* recursive sorting */
func foo(inout remainingCustomers: [Customer], inout _ sortedCustomers: [Customer], _ parentId: Int? = nil) {
let children = remainingCustomers.filter { $0.parentId == parentId }
for child in children {
sortedCustomers.append(child)
foo(&remainingCustomers, &sortedCustomers, child.id)
}
remainingCustomers = remainingCustomers.filter { $0.parentId != parentId }
}
使用您的客户数组(例如customer
)和一个空数组(比如sortedCustomers
)调用,其中后者是将附加已排序客户的结果数组。
/* example setup */
struct Customer {
let id: Int
let parentId: Int?
}
var customers : [Customer] = [
Customer(id: 13, parentId: 2),
Customer(id: 14, parentId: 5),
Customer(id: 1, parentId: nil),
Customer(id: 2, parentId: 3),
Customer(id: 7, parentId: 2),
Customer(id: 3, parentId: 1),
Customer(id: 4, parentId: 14),
Customer(id: 5, parentId: nil),
Customer(id: 6, parentId: 1)]
/* example usage */
var sortedCustomers : [Customer] = []
foo(&customers, &sortedCustomers)
sortedCustomers.forEach { print($0) }
/* Customer(id: 1, parentId: nil)
Customer(id: 3, parentId: Optional(1))
Customer(id: 2, parentId: Optional(3))
Customer(id: 13, parentId: Optional(2))
Customer(id: 7, parentId: Optional(2))
Customer(id: 6, parentId: Optional(1))
Customer(id: 5, parentId: nil)
Customer(id: 14, parentId: Optional(5))
Customer(id: 4, parentId: Optional(14)) */
请注意,上面的内容并不是非常优化的w.r.t.性能(例如foo
中的双重过滤)随customers
数组大小的增长而增加。由于您的排序的上下文是一个要以用户身份显示的数组,我假设您不会将上述内容用于大型数组。如果这成为问题,Knuth would advice us只会从此算法的成熟优化开始。
为了便于查看结果,您可以在递归中包含depth
属性,例如如下所示打印子项(同时附加到sortedCustomers
),其中一些填充与递归深度成比例:
/* recursive sorting */
func foo(inout remainingCustomers: [Customer], inout _ sortedCustomers: [Customer], _ parentId: Int? = nil, _ depth: Int = 0) {
let children = remainingCustomers.filter { $0.parentId == parentId }
for child in children {
sortedCustomers.append(child)
let padding = String(count: 4*depth, repeatedValue: (" " as Character))
print(padding + "\(child)")
foo(&remainingCustomers, &sortedCustomers, child.id, depth+1)
}
remainingCustomers = remainingCustomers.filter { $0.parentId != parentId }
}
/* example usage */
var sortedCustomers : [Customer] = []
foo(&customers, &sortedCustomers)
/*
Customer(id: 1, parentId: nil)
Customer(id: 3, parentId: Optional(1))
Customer(id: 2, parentId: Optional(3))
Customer(id: 13, parentId: Optional(2))
Customer(id: 7, parentId: Optional(2))
Customer(id: 6, parentId: Optional(1))
Customer(id: 5, parentId: nil)
Customer(id: 14, parentId: Optional(5))
Customer(id: 4, parentId: Optional(14)) */
答案 1 :(得分:1)
由于您需要缩进级别和订单,因此您应该提前计划并准备缩进级别以及排序。
您可以通过首先向家长添加家属(或者如果您无法控制Customer
类,在父母之外构建家属),然后在树深度前行,以相对简单的方式执行此操作,并将结果存储在拓扑顺序。
这是一个如何做到这一点的例子。它使用此Customer
类:
public class Customer : CustomStringConvertible {
public var Dependents = [Customer]()
public let Id : Int
public let Parent : Int?
public init(_ id:Int, _ parent:Int?) {
Id = id
Parent = parent
}
public var description: String {
return "Customer Id=\(Id) Parent=\(Parent)"
}
}
请注意每个Customer
如何包含Dependents
数组,以及自己的ID和其父级的可选ID。
在排序之前,您需要为每个客户添加依赖项。您可以通过两次forEach
:
let cust = [ Customer(2, 3), Customer(3, 1), Customer(1, nil), Customer(4, 1), Customer(5, 3)]
var byId = [Int:Customer]()
// Add each customer to dictionary by ID
cust.forEach { byId[$0.Id] = $0 }
// Go throug customers again, this time adding them to their parents
cust.forEach { c in
if let parentId = c.Parent {
byId[parentId]?.Dependents.append(c)
}
}
使用byId
,您可以为结果准备数组,并开始排序。我将使用(Customer, Int)
的元组来表示客户及其缩进级别:
var sorted = [(Customer,Int)]()
// Recursive local function for adding dependents to the result
func store(customer:Customer, _ level:Int) {
sorted.append((customer, level))
customer.Dependents.forEach { store($0, level+1) }
}
// Call the recursive function for each top-level customer:
for c in cust.filter({ $0.Parent == nil }) {
store(c, 0)
}
那就是它 - 现在我们可以用缩进打印结果,如下所示:
for (customer, level) in sorted {
let indent = String(count:level, repeatedValue:Character(" "))
print("\(indent) \(customer)")
}