2013-06-02 13 views
10
$scope.clearCompleted = function() 
     { 
      angular.forEach($scope.todos, function(todo, i) 
      { 
       if(todo.done) 
       { 
        $scope.todos.splice(i, 1); 
       } 
      }); 

      if($scope.todos.length == 0) 
      { 
       $scope.isEmpty = true; 
      } 
     } 

Questo è il mio codice per eliminare il 'fatto' todos da una matrice, ma quando due todos dopo l'altro vengono rimossi, si rimuove solo il secondo. Penso che sia perché la funzione splice si resetta e restituisce l'array spliced.JavaScript funzione splice all'interno ciclo foreach decrementa indice

+0

Sì, '.splice()' muta di un array. Questo deve essere tenuto in considerazione se si utilizza una iterazione diretta. –

risposta

18

Si uniscono gli elementi dell'array, che è stato iterato, pertanto gli indici in "todos" sono stati ridotti. Scusa per il mio cattivo inglese.

var notDonedTodos = []; 
angular.forEach($scope.todos, function(todo, i) 
{ 
    if(!todo.done) 
    { 
     notDonedTodos.push(todo); 
    } 
}); 

$scope.todos = notDonedTodos; 
+0

Grazie amico, sei fantastico! – jvakuiler

18

Questo sta accadendo perché forEach conosce solo lo stato iniziale della matrice, e chiede pertanto il metodo di due volte, anche se la prima chiamata rimuove un elemento dalla matrice. Basta fare un semplice ciclo while invece:

var i = $scope.todos.length; 
while (i--){ 
    if ($scope.todos[i].done){ 
     $scope.todos.splice(i, 1); 
    } 
} 
+0

Vale la pena sottolineare che il trucco in questa soluzione è che l'array viene elaborato in ordine inverso, consentendoci di ignorare la volatilità della lunghezza dell'array. Questo è ugualmente valido anche con un ciclo for tradizionale. –

3

Il problema con il each iterazione è che essa rimuove un elemento dalla matrice causando un iterazione da omettere. jQuery ha un buon metodo grep che restituisce tutti gli elementi che corrispondono a un determinato criterio determinato da una funzione anonima fornita.

var todos =[{id:1, done:false},{id:2, done:true},{id:3, done:true}]; 

function removeCompleted(todos){ 
    return $.grep(todos,function(todo){ 
     return todo.done == false; 
    }); 
} 

todos = removeCompleted(todos); 
console.log(todos); 

lavoro Esempiohttp://jsfiddle.net/ktCEN/

Documentation

2

Come ulteriore alternativa, si può solo diminuire l'indice ogni volta che fate un splice. Ad esempio:

$scope.clearCompleted = function() { 
    angular.forEach($scope.todos, function(todo, i) { 
     if(todo.done) { 
      $scope.todos.splice(i, 1); 
      i--; 
     }; 
    }); 

    if($scope.todos.length == 0) { 
     $scope.isEmpty = true; 
    }; 
} 

Questo regola l'indice per mantenere la sua validità ogni volta che la matrice viene modificata. È ancora possibile utilizzare angular.forEach e non si ottengono due copie dell'array.

7

L'alternativa che ho trovato è utilizzare il metodo array.filter. È il modo più semplice per filtrare un array in base alle chiavi dell'oggetto. Se stai lavorando su un progetto IE8 (povero te) dovrai aggiungere un polyfill per questa funzione dato che è abbastanza nuovo per JavaScript.

Everything you need to know about javascript.

codice di risposta:

$scope.clearCompleted = function() { 
    $scope.todos = $scope.todos.filter(function(item) { 
     return !item.done; 
    }); 
} 
+0

Ottima soluzione – Clint