How to identify object that is deleted from a collection (array)?

Viewed 119

I have following code to handle pull to refresh response

let copyData = data.reversed() // it is pull to refresh response (load page 1)
for (_,element) in copyData.enumerated() {
    let foundElement = allObjects.filter{$0.id == element.id} // Find  element in main array
    if let firstElement = foundElement.first, let index =   allObjects.index(of: firstElement) {
        allObjects[index] = element // Replace if found
    } else {
        allObjects.insert(element, at: 0) // Insert if not found 
    }
}


self.arrayPosts = allObjects 

Where data is codable class which is API response of pull to refresh. allObjects is preloaded data with pagination

Question : Suppose In allObjects i have 50 Object (5 Pages of 10 ID is (1 to 50)) User pull to refresh And I load first Page from API (ID 1,2,3,4,5,6,7,10,11) then how to identify which object is deleted (8,9) ?

Should I compare allObjects 's 10th index with data's 10th index object's ID ?

Is it better way to handle this ? Please suggest

3 Answers

Don't compare pages (i.e. 10 items at a time) - if an item is added/deleted the pages will get out of sync, and you'll end up with missing / duplicate objects.

Presumably your objects are sorted by some key / date, etc.

  • Take the value of the key in your last downloaded object.
  • Copy all the existing objects with keys <= that last key into a new array.
  • Compare your downloaded array against this sub-array.
  • Objects in the downloaded array that are not in the sub-array should be removed.

Here How I handle this. Code is quite complex for first time read but added comments to understand it

func handleResponse(page:Int,isForRefersh:Bool = false, data:Array<InspirePost>) {


        guard data.count != 0 else {
            // Check if we are requesting data from pull to referesh and First page is empty then we don't have data to show change state to empty

            if self.arrayPosts.count == 0  || (isForRefersh && page == 1) {
                self.state = .empty
                self.tableView.reloadData()

            } else {
                self.state = .populated(posts: self.arrayPosts)

            }
            return
        }

        // Now we need to check if data called by referesh control then
        //1) Replace object in array other wise just append it.

        var allObjects = self.state.currentPost

        if isForRefersh {
            // If both array Has same number of element i.e both has page one loaded
            if data.count >= allObjects.count {
                allObjects = data
            } else {
                let copyData = data.reversed()
                for (_,element) in copyData.enumerated() {
                    if let index = allObjects.firstIndex(where: {$0.id == element.id}) {
                        allObjects[index] = element // Replace if found
                    } else {
                        allObjects.insert(element, at: 0) // Insert if not
                    }

                }

                let minID = data.min(by: {$0.id ?? 0 < $1.id ?? 0})?.id

                // DELETE item
                let copyAllObject = allObjects
                for (_,element) in copyAllObject.enumerated() {
                    guard let id = element.id, id >=  minID  ?? 0 else {
                        continue
                    }
                    if !data.contains(element) {
                        if let indexInMainArray = allObjects.index(where: {$0.id == id}) {
                            allObjects.remove(at: indexInMainArray)

                        }
                    }
                }
            }

            //When we pull to refersh check the curent state

            switch self.state {
            case .empty,.populated : // if empty or populated set it as populated (empty if no record was avaiable then pull to refersh )
                self.state = .populated(posts: allObjects)
            case .error(_, let  lastState) : // If there was error before pull to referesh handle this
                switch lastState {
                case .empty ,.populated: // Before the error tableview was empty or popluated with data
                    self.state = .populated(posts: allObjects)
                case .loading,.error: // Before error there was loading data (There might more pages if it was in loading so we doing paging state ) or error
                    self.state = .paging(posts: allObjects, nextPage: page + 1)
                case .paging(_,let nextPage): // Before error there was paging then we again change it to paging
                    self.state = .paging(posts: allObjects, nextPage: nextPage)


                }
            case .loading: // Current state was loading (this might not been true but for safety we are adding this)
                self.state = .paging(posts: allObjects, nextPage: page + 1)
            case .paging(_,let nextPage): // if there was paging on going don't break anything
                self.state = .paging(posts: allObjects, nextPage: nextPage)

            }

            self.arrayPosts = allObjects



        } else {
            allObjects.append(contentsOf: data)
            self.isMoreDataAvailable = data.count >= self.pageLimit

            if self.isMoreDataAvailable {

                self.state = .paging(posts: allObjects, nextPage: page + 1)

            } else {
                self.state = .populated(posts: allObjects)
            }
            self.arrayPosts = self.state.currentPost
        }

        self.tableView.reloadData()

    }

Where I have

indirect enum PostListStatus {
    case loading
    case paging(posts:[InspirePost],nextPage:Int)
    case populated(posts:[InspirePost])
    case error (error:String,lastState:PostListStatus) // keep last state for future if we need to know about data or state
    case empty


    var currentPost:[InspirePost] {
        switch self {
        case .paging(let posts ,  _):
            return posts
        case .populated( let posts):
            return posts
        case .error( _, let  oldPost) :
            switch oldPost {
            case .paging(let posts ,  _):
                return posts
            case .populated( let posts):
                return posts
            default:
                return []
            }

        default:
            return []
        }
    }

    var nextPage : Int {
        switch  self {
        case .paging(_, let page):
            return page
        default:
            return 1
        }
    }

}

You may make set for both allObjects and data. Then use subtracting(_:) method in set to find the missing one. Remove those missing ones from the main array and use it. Once you have correct main array elements, page them while displaying.

Related