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.
CodePudding user response:
As with most things to do with trees, recursion is going to help. You can add an extra parameter that indicates which array of CustomObject
s 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: ...)