代码之家  ›  专栏  ›  技术社区  ›  Mark A. Donohoe

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

  •  0
  • Mark A. Donohoe  · 技术社区  · 6 年前

    通常在我的各种项目中,我必须处理分层数据。在某些情况下,我需要通过使用递归函数在层次结构中爬行来“扁平”数据。我总是很沮丧,因为我必须编写所有的代码来实现一些我认为应该很简单的东西。

    由于Swift能够编写自定义序列类,所以我决定看看是否可以编写一个以可重用的方式实现此目标的序列类。

    在搜索了所有stackoverflow,收集了各种片段,最后用迭代器计算出递归之后,我已经成功地找到了一个有效的解决方案。因为它是从这里诞生的,所以我想回馈社区并分享它,希望它能像网站帮助我一样帮助别人。

    也就是说,为了遵循这样的实践,我把它作为一个问题/答案发布。因此,见下文。

    享受!:)

    1 回复  |  直到 6 年前
        1
  •  0
  •   Mark A. Donohoe    6 年前

    如上所述,我编写了一个类,它允许您遍历一组分层数据,同时保持该层次结构的有序性。通过指定根元素和返回每个根的子元素的闭包,可以做到这一点。通过递归,实现不仅以正确的层次顺序返回内容,而且还返回一个级别,这样您就知道这些项的深度了。

    这是密码……

    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
    

    我很想得到一些反馈,看看是否有一个更容易的方法来实现这一点,但我不得不说,我对性能很满意。

    让我知道你的想法!