Search code examples
arraysforeachdojostore

Can I remove objects in a store using dojo.forEach in a single loop?


I want to loop through the objects in a store and remove them if they don't meet some condition, ex:

this.myStore = new Observable(new Memory({identifier: "id", data: []}));

...

array.forEach(this.myStore.data, lang.hitch(this, function(foo){
    if (foo.status === Status.REMOVE){
        this.myStore.remove(foo.id);
    }
    else{
        this.myStore.get(foo.id).status = Status.NONE;
    }
}));

However as soon as I remove a single object in the store, the foo object becomes undefined on the next loop.

Should this be possible or is this the wrong way to do it?

A workaround around I found is to first loop to find the id of the objects I want to remove, and store those in an array. Then I loop a second time on this id array to remove the objects instead of looping on the store data itself. It works, but must I loop twice to do something like this?

Thanks


Solution

  • The Short Answer

    Use store.query().forEach(...) instead.

    The Long Answer

    store.data is the property that dojo/store/Memory uses internally to store and act upon the data in the store. It is specific to the Memory store implementation and should generally not be iterated upon directly, especially in these situations, since store.remove is going to splice elements out of this array, and iterating over an array as its elements are spliced out of itself has interesting undesirable effects:

    • With ES5's Array#forEach, your loop will skip an item each time one at the same or lower index is removed, since it proceeds sequentially through array indices without ever suspecting that items are being spliced out from under it.
    • With dojo/_base/array.forEach, you get the item-skipping behavior and errors, because the functions in dojo/_base/array cache the length of the array first and just keep going, not expecting that number to change.

    On the other hand, store.query() is intended for use cases where you want to iterate over data from your store. The QueryResults object it returns even provides forEach and map functions that you can use regardless of whether you're in an ES5-ready browser and regardless of whether your store is even synchronous (it will run asynchronously once results are retrieved for asynchronous stores). But most importantly, it returns its own array in the case of Memory, so you don't have to deal with array elements being spliced out from under you.

    Runnable example: http://jsfiddle.net/eu6aqxbq/