Как создать последовательность Swift на основе иерархических данных? - PullRequest
0 голосов
/ 19 сентября 2018

Обычно в моих различных проектах мне приходится иметь дело с иерархическими данными.Есть случаи, когда мне нужно «сгладить» эти данные, по существу обходя иерархию с помощью рекурсивных функций.Меня всегда огорчало, что я должен был написать весь этот код, чтобы достичь чего-то, что, по моему мнению, должно быть простым.

Поскольку Swift имеет возможность писать собственные классы Sequence, я решил посмотреть, смогу ли я написать тот, который будетдостигните этой цели способом многократного использования.

После того, как мы прочесали весь StackOverflow, собрали различные фрагменты и, наконец, выяснили рекурсию с помощью итераторов, я успешно нашел рабочее решение.Так как он родился отсюда, я хотел отдать его сообществу и поделиться им, надеясь, что он может помочь другим так, как сайт всегда помогает мне.

Тем не менее, чтобы следовать SO практикам, я публикуюэто как вопрос / ответ.Поэтому см. Ниже.

Наслаждайтесь!:)

1 Ответ

0 голосов
/ 19 сентября 2018

Как указано выше, я написал класс, который позволяет вам перебирать иерархический набор данных, сохраняя эту иерархию в порядке.Это делается путем указания корневых элементов и замыкания, которое возвращает дочерние элементы для каждого из корней.Реализация через рекурсию не только возвращает вещи в правильном иерархическом порядке, но также возвращает уровень, чтобы вы знали, насколько глубоки были элементы.

Вот код ...

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

Я хотел бы получить некоторые отзывы об этом, чтобы увидеть, если естьвозможно, более простой способ добиться этого, но я должен сказать, что я доволен производительностью.

Дай мне знать, что ты думаешь!

...