Home > front end >  Nested list string from an array of CoreData one-to-many relationship objects in Swift
Nested list string from an array of CoreData one-to-many relationship objects in Swift

Time:08-18

How do I get a nested list as a string from an array of Core Data model with a one-to-many relationship.

coredata model relationships

import Foundation
import CoreData

@objc(Node)
public class Node: NSManagedObject {

}

extension Node {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Node> {
        return NSFetchRequest<Node>(entityName: "Node")
    }

    @NSManaged public var text: String?
    @NSManaged public var children: Set<Node>
    @NSManaged public var parent: Node?
    @NSManaged public var orderNo: Int16

     public var childrenArray: [Node]{
        let set = children

        return set.sorted {
            $0.orderNo < $1.orderNo
        }
    }
    
}

Here's what I've tried so far:

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Node.orderNo, ascending: true),NSSortDescriptor(keyPath: \Node.dateAdded, ascending: true)],
    predicate: NSCompoundPredicate(type: .and, subpredicates: [
        NSPredicate(format: "parent == nil")
       // NSPredicate(format: "completed = %d", false)
    ]),
    
    animation: .default
) private var nodes: FetchedResults<Node>


for node in nodes{
    var current = node

    while current.childrenArray.count>0{
      for child in current.childrenArray{
          if let text = child.text{
               print(text)
           }
           current = child
       }
    }
}

The limitation of this is that once childrenArray.count is 0, instead of "skipping" that node loop stops there. Also, with this method, I can't figure out a way to indent the child nodes as in a typical nested list.

CodePudding user response:

To solve this you will need a recursive function instead of a loops within loops solution. The recursive function can then call itself in a loop with the current Node as a new parent node.

extension Node {
    func printTree(indentSize: Int = 0) {
        let indent = indentSize == 0 ? "" : String(repeating: " ", count: indentSize)
        print("\(indent)\(text)")
        for child in children {
            child.printTree(indentSize: indentSize   2)
        }
    }
}

Here is an example with a simplified version of Node


struct Node {
    let text: String
    let children: [Node]
}

let root = Node(text: "Root", children: [
    Node(text: "child 1:1", children: [Node(text: "child 2:1:1", children: [])]),
    Node(text: "child 1:2", children: [
        Node(text: "child 2:2:1", children: []),
        Node(text: "child 2:2:2", children: [Node(text: "child 2:2:2:3", children: [])])
    ])
])

root.printTree()

Output

Root  
  child 1:1  
    child 2:1:1  
  child 1:2  
    child 2:2:1  
    child 2:2:2  
      child 2:2:2:3
  • Related