如何基于分层数据创建Swift序列?

时间:2018-09-18 23:39:48

标签: swift sequence hierarchy hierarchical-data

常规地,在我的各个项目中,我必须处理分层数据。在某些情况下,我需要通过本质上使用递归函数在层次结构中进行爬网来“整理”数据。我总是不得不写所有代码才能实现我认为应该很简单的东西,这总是让我感到沮丧。

由于Swift具有编写自定义Sequence类的能力,因此我决定看看是否可以编写一种可以以可重用的方式实现此目标的类。

在遍历StackOverflow之后,收集了各个部分,最后使用迭代器弄清楚了递归之后,我成功地提出了一个可行的解决方案。由于它是从这里诞生的,所以我想回馈社区并分享它,希望它可以像站点始终为我提供帮助的方式对他人有所帮助。

也就是说,为了遵循SO惯例,我将其发布为问题/答案。因此,请参见下文。

享受! :)

1 个答案:

答案 0 :(得分:0)

如上所述,我编写了一个类,该类允许您遍历层次结构的数据集,同时保持层次结构的顺序。通过指定根元素和一个闭包(返回每个根的子元素)来执行此操作。通过递归的实现,不仅以正确的层次结构顺序返回事物,而且还返回一个级别,以便您了解项目的深度。

这是代码...

struct HierarchicalSequence<TItem> : Sequence {

    typealias GetChildItemsDelegate = (TItem) -> [TItem]

    init(rootItems:[TItem], getChildItems: GetChildItemsDelegate? = nil){
        self.rootItems     = rootItems
        self.getChildItems = getChildItems
    }

    let rootItems     : [TItem]
    let getChildItems : GetChildItemsDelegate?

    class Iterator : IteratorProtocol {

        typealias Element = (level:Int, item:TItem)

        init(level:Int, items:[TItem], getChildItems: GetChildItemsDelegate? = nil){
            self.level         = level
            self.items         = items
            self.getChildItems = getChildItems
        }

        let level         : Int
        let items         : [TItem]
        let getChildItems : GetChildItemsDelegate?

        private var nextIndex = 0

        var childIterator:Iterator?

        func next() -> Element? {

            if let childIterator = childIterator {

                if let childIteratorResult = childIterator.next(){
                    return childIteratorResult
                }

                self.childIterator = nil
            }

            if nextIndex == items.count {
                return nil
            }

            let item = items[nextIndex]
            nextIndex += 1

            if let getChildItems = getChildItems {

                let childItems = getChildItems(item)

                childIterator = Iterator(
                    level         : level + 1,
                    items         : childItems,
                    getChildItems : getChildItems)
            }

            return (level, item)
        }
    }

    func makeIterator() -> Iterator {
        return Iterator(level: 0, items: rootItems, getChildItems: getChildItems)
    }
}

这是一个被使用的例子。

public let jsonString = """
    [
        {
            "name" : "Section A",
            "subCategories" : [
                {
                    "name" : "Category A1",
                    "subCategories" : [
                        { "name" : "Component A1a" },
                        { "name" : "Component A1b" }
                    ]
                },
                {
                    "name" : "Category A2",
                    "subCategories" : [
                        { "name" : "Component A2a" },
                        { "name" : "Component A2b" }
                    ]
                }
            ]
        },
        {
            "name" : "Section B",
            "subCategories" : [
                {
                    "name" : "Category B1",
                    "subCategories" : [
                        { "name" : "Component B1a" },
                        { "name" : "Component B1b" }
                    ]
                },
                {
                    "name" : "Category B2",
                    "subCategories" : [
                        { "name" : "Component B2a" },
                        { "name" : "Component B2b" }
                    ]
                }
            ]
        }
    ]
    """

public let jsonData = jsonString.data(using: .utf8)!

class Category : Codable {

    required init(from decoder: Decoder) throws {

        let values = try decoder.container(keyedBy: CodingKeys.self)

        name          = try values.decode(String.self, forKey: .name)
        subCategories = try values.decodeIfPresent([Category].self, forKey: .subCategories) ?? []
    }

    let name          : String
    let subCategories : [Category]
}

var myJsonCategories = try! JSONDecoder().decode([Category].self, from: jsonData)

let hierarchicalCategories = HierarchicalSequence(rootItems:myJsonCategories){
    category in category.subCategories
}

for categoryInfo in hierarchicalCategories {
    print("\(String(repeating: " ", count: categoryInfo.level * 2))\(categoryInfo.level):\(categoryInfo.item.name)")
}

最后,这是输出...

0:Section A
  1:Category A1
    2:Component A1a
    2:Component A1b
  1:Category A2
    2:Component A2a
    2:Component A2b
0:Section B
  1:Category B1
    2:Component B1a
    2:Component B1b
  1:Category B2
    2:Component B2a
    2:Component B2b

我很乐意对此提供一些反馈,以查看是否有更简单的方法可以实现这一目标,但是我不得不说我对性能感到满意。

莱姆知道你的想法!