[SOLVED] Swift – How to update object in multi-dimensional directory

Issue

I want to be able to find and update a custom object in an array of these objects. The challenge is that the custom objects also can be children of the object.

The custom object looks like this:

class CustomObject: NSObject {
    var id: String?
    var title: String?
    var childObjects: [CustomObject]?
}

I would like to be able to create a function that overwrites the custom object with fx a specific ID, like this:

 var allCustomObjects: [CustomObject]?
    
 func updateCustomObject(withId id: String, newCustomObject: CustomObject) {
        
     var updatedAllCustomObjects = allCustomObjects
        
     // ...
     // find and update the specific custom object with the id
     // ...
        
     allCustomObjects = updatedAllCustomObjects
 }

I recognize this must be a pretty normal issue regarding multidimensional arrays / directories in both Swift and other languages. Please let me know what normal practice is used for this issue.

Solution

As with most things to do with trees, recursion is going to help. You can add an extra parameter that indicates which array of CustomObjects that you are currently going through, and returns a Bool indicating whether the ID is found, for short-circuiting purposes.

@discardableResult
func updateCustomObject(withId id: String, in objectsOrNil: inout [CustomObject]?, newCustomObject: CustomObject) -> Bool {
    guard let objects = objectsOrNil else { return false }
    if let index = objects.firstIndex(where: { $0.id == id }) {
        // base case: if we can find the ID directly in the array passed in
        objectsOrNil?[index] = newCustomObject
        return true
    } else { 
        // recursive case: we need to do the same thing for the children of
        // each of the objects in the array
        for obj in objects {
            // if an update is successful, we can end the loop there!
            if updateCustomObject(withId: id, in: &obj.childObjects, newCustomObject: newCustomObject) {
                return true
            }
        }
        return false
        // technically I think you can also replace the loop with a call to "contains":
        // return objects.contains(where: { 
        //     updateCustomObject(withId: id, in: &$0.childObjects, newCustomObject: newCustomObject) 
        //  })
        // but I don't like doing that because updateCustomObject has side effects
    }
}

You would call this like this, with the in: parameter being allCustomObjects.

updateCustomObject(withId: "...", in: &allCustomObjects, newCustomObject: ...)

Answered By – Sweeper

Answer Checked By – Timothy Miller (BugsFixing Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *