Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript .map on an array and removing items if condition satisfied

I have an array queue that I push objects to it when they are modified. If the user presses save, then I will loop through the queue and apply the appropriate API call for them.

If the API call goes through successfully, I want to remove the item from the queue, otherwise keep it inside and notify the user that some items were not successfully saved. I currently have this (in AngularJS)

var unsuccessfulItems = [];
var promise = queue.map(function(item) {
    var defer = $q.defer();
    myCallFunction( item
           , function( response ) {} // Success
           , function( response ) {  // Error
               unsuccessfulItems.push(item);
           }
    )
    defer.resolve();
    return defer.promise;
})
// Once all items have been processed
$q.all( promise ).then( function() {
    queue = unsuccessfulItems;
});

Is there a better way of doing this?

like image 765
Kousha Avatar asked Apr 26 '14 23:04

Kousha


People also ask

How do you remove an element from an array based on condition?

To remove elements from ArrayList based on a condition or predicate or filter, use removeIf() method. You can call removeIf() method on the ArrayList, with the predicate (filter) passed as argument. All the elements that satisfy the filter (predicate) will be removed from the ArrayList.

Is .map destructive?

map() is a non-destructive method.

Does .map always return an array?

As discussed, map() method always returns a new array, so if you don't have any use of a new array then never use map() method.

How do you remove an element from an array based on a condition in JS?

shift() function: This method is use to remove elements from the start of an array. splice() function: This method is use to remove elements from the specific index of an array. filter() function: This method is use to remove elements in programmatically way.


1 Answers

You're already using promises, you might want to do it end-to-end. Also, you're resolving the promise too early.

Assuming the suboptimal case where you don't want to promisify myCallFunction itself, you should still promisify it.

function myCall(item){
    var d = $q.defer();
    myCallFunction(item,function(r){ d.resolve({val:r,item:item});}
                       ,function(r){ d.reject(r);});
    return d.promise;
}

Note, we are resolving the defer after the asynchronous function is done, not before it.

Now, we need to implement a "Settle" function, that resolves when all promises are done no matter what. This is like $q.all but will wait for all promises to resolve and not fulfill.

function settle(promises){
     var d = $q.defer();
     var counter = 0;
     var results = Array(promises.length);
     promises.forEach(function(p,i){ 
         p.then(function(v){ // add as fulfilled
              results[i] = {state:"fulfilled", promise : p, value: v};
         }).catch(function(r){ // add as rejected
              results[i] = {state:"rejected", promise : p, reason: r};
         }).finally(function(){  // when any promises resolved or failed
             counter++; // notify the counter
             if (counter === promises.length) {
                d.resolve(results); // resolve the deferred.
             }
         });
     });
}

This sort of settle function exists in most promise implementations but not in $q. We could have also done this with rejections and $q.all, but that would mean exceptions for flow control which is a bad practice.

Now, we can settle:

 settle(queue.map(myCall)).then(function(results){
     var failed = results.filter(function(r){ return r.state === "rejected"; });
     var failedItems = failed.map(function(i){ return i.value.item; });
 });
like image 141
Benjamin Gruenbaum Avatar answered Sep 28 '22 06:09

Benjamin Gruenbaum