【问题标题】:How do you create a Swift sequence based on hierarchical data?如何基于分层数据创建 Swift 序列?
【发布时间】:2018-09-18 23:39:48
【问题描述】:

在我的各种项目中,我经常需要处理分层数据的迭代。尽管它很常见,但我不得不编写这么多样板代码来做这件事总是让我感到沮丧。

感谢 Swift 编写自定义序列类的能力,我决定看看我是否可以编写一个以可重用的方式实现这一目标的类。下面是我的结果。

我决定根据 Jeff Atwood 的 [自己的 cmets 鼓励发布您自己的答案][1] 在此处发布此内容,他说...

不仅可以提出和回答自己的问题,而且明确鼓励 [...] 我一直这样做!

因此,我在这里提供此解决方案,希望在其他人搜索此站点时有所帮助。

享受吧! :)

【问题讨论】:

  • 我投票结束这个问题,因为它属于codereview.stackexchange.com
  • 我可以尊重你的观点,但我之所以把它放在这里是因为这是人们访问此类信息的站点。虽然是的。我没有问题,这并不意味着它不是对其他人有用/有帮助的信息。但同样,我确实理解你的观点。
  • 在一天结束时,我只是一票,但你真的要求评论 你的答案 ... 这里离题,会导致很多基于意见的其他答案,这是我们关闭问题的另一个原因。我不会对您对问题的解决方案发表任何评论,只是我认为交付不适合这种格式。
  • 嗯,反馈是次要的帖子的意图,即展示如何使用这种序列类型迭代任何分层数据,这是我一直在寻找的东西.反馈只是为了看看我是否遗漏了什么或者是否有更好的方法,但正如我所说,我对结果很满意。
  • 这里没有明确的问题,所以这可能对极少数人有帮助。如果您自己回答问题,请花时间把问题弄清楚。

标签: swift sequence hierarchy hierarchical-data


【解决方案1】:

如上所述,我编写了一个类,它允许您迭代一组分层数据,同时保持该层次结构有序。您可以通过指定一个或多个根元素(通过数组或可变参数)以及返回给定元素的子元素的闭包来做到这一点。

由于它是作为泛型实现的,如果您知道层次结构是同质的,则可以指定要使用的显式类型,但如果不是,请为该类型指定 Any,然后在闭包中执行逻辑以确定哪个子级输入它。

此外,通过递归实现,不仅以正确的层次顺序返回事物,而且还返回一个级别,以便您知道项目的深度。如果不关心关卡,只需在初始化序列时追加.map{ $0.item }即可直接提取项目。

这是自定义分层序列的代码...

struct HierarchicalSequence<TItem> : Sequence {

    typealias GetChildItemsDelegate = (TItem) -> [TItem]?

    init(_ rootItems:TItem..., getChildItems: @escaping GetChildItemsDelegate){
        self.init(rootItems, getChildItems: getChildItems)
    }
    init(rootItems:[TItem], getChildItems: @escaping GetChildItemsDelegate){
        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: @escaping GetChildItemsDelegate){
            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 there's a child iterator, use it to see if there's a 'next' item
            if let childIterator = childIterator {

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

                // No more children so let's clear out the iterator
                self.childIterator = nil
            }

            if nextIndex == items.count {
                return nil
            }

            let item = items[nextIndex]
            nextIndex += 1

            // Set up the child iterator for the next call to 'next' but still return 'item' from this call
            if let childItems = getChildItems(item),
                childItems.count > 0 {

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

            return (level, item)
        }
    }

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

让我们看一个如何使用它的示例。首先,让我们从一些 JSON 数据开始...

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" }
                    ]
                }
            ]
        }
    ]
    """

这是加载该数据的模型和代码

class Category : Codable {
    let name          : String
    let subCategories : [Category]?
}

public let jsonData = jsonString.data(using: .utf8)!
var rootCategories = try! JSONDecoder().decode([Category].self, from: jsonData)

这是您如何使用序列获取所有类别及其深度...

let allCategoriesWithDepth = HierarchicalSequence(rootItems:rootCategories){ $0.subCategories }

for (depth, category) in allCategoriesWithDepth {
    print("\(String(repeating: " ", count: depth * 2))\(depth): \(category.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

享受吧!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-11
    • 2015-03-19
    相关资源
    最近更新 更多