在UITableView的分层结构中对数组进行排序

时间:2016-04-14 20:03:02

标签: arrays swift sorting

我有一组客户对象,每个客户对象都有自己的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)
]

然后,我将使用它来缩进表格视图单元格以显示客户的层次结构。

我见过像thisthis这样的问题,但我不知道如何将其应用于Swift。我怎样才能对数组进行排序?

2 个答案:

答案 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)")
}