2013-06-02 12 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; 
      } 
     } 

To jest mój kod, aby usunąć „done” todos z tablicy, ale gdy dwa todos po drugim są usuwane, usuwa tylko drugi. Myślę, że dzieje się tak, ponieważ funkcja splicingu resetuje się i zwraca tablicę złożoną z połączeń.JavaScript funkcja splice wewnątrz pętli foreach zmniejsza indeks

+0

Tak, '.splice()' mutuje tablicy. To musi być uwzględnione, jeśli korzystasz z iteracji forward. –

Odpowiedz

18

Łączysz elementy z tablicy, która została poddana iteracji, dlatego indeksy w "todos" zostały zredukowane. Przepraszam za mój zły angielski.

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

$scope.todos = notDonedTodos; 
+0

Dzięki stary, jesteś świetny! – jvakuiler

18

Dzieje się tak dlatego, forEach wie tylko o stanie początkowym tablicy, a zatem wywołuje metodę dwukrotnie, nawet jeżeli pierwsze wywołanie usuwa element z tablicy. Wystarczy zrobić prosty pętli while zamiast:

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

Warto zauważyć, że sztuczka w tym rozwiązaniu polega na tym, że tablica jest przetwarzana w odwrotnej kolejności, co pozwala nam zignorować zmienność długości tablicy. Jest to równie ważne w przypadku tradycyjnej pętli for. –

3

Problem z each iteracji jest to, że usuwa element z tablicy powodując iteracja zostać pominięte. jQuery ma ładną metodę grep, która zwraca wszystkie elementy pasujące do określonych kryteriów, które są określone przez dostarczoną anonimową funkcję.

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); 

Przykład roboczyhttp://jsfiddle.net/ktCEN/

Documentation

2

Jako inną alternatywę, można po prostu zmniejszyć swoją indeks za każdym razem robisz splice. Na przykład:

$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; 
    }; 
} 

ten dostosowuje indeks utrzymać swoją ważność każdym razem, gdy tablica jest modyfikowany. Nadal możesz używać angular.forEach, a nie skończysz z dwiema kopiami swojej tablicy.

7

Alternatywą, którą znalazłem, jest użycie metody array.filter. Jest to najprostszy sposób filtrowania tablicy na podstawie kluczy obiektów. Jeśli pracujesz nad projektem IE8 (kiepsko), będziesz musiał dodać polyfill dla tej funkcji, ponieważ jest on całkiem nowy w JavaScript.

Everything you need to know about javascript.

kod Odpowiedź:

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

Doskonałe rozwiązanie – Clint

Powiązane problemy