Функция склейки JavaScript внутри индекса сокращений цикла foreach

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

Это мой код для удаления 'done' todos из массива, но когда удаляются два тодоса друг за другом, он удаляет только второй. Я думаю, это потому, что функция сращивания сбрасывается и возвращает сплайсированный массив.

Ответ 1

Вы соединяете элементы из массива, которые вы повторили, поэтому индексы в "todos" уменьшены. Извините за мой плохой английский.

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

$scope.todos = notDonedTodos;

Ответ 2

Это происходит потому, что forEach знает только о начальном состоянии массива и поэтому дважды вызывает ваш метод, даже если первый вызов удаляет элемент из массива. Просто выполните простой цикл while:

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

Ответ 3

Альтернативой, которую я нашел, является использование метода array.filter. Это самый простой способ фильтрации массива на основе ключей объектов. Если вы работаете над проектом IE8 (вам плохо), вам нужно добавить polyfill для этой функции, поскольку он довольно новичок в JavaScript.

Все, что вам нужно знать о javascript.

Код ответа:

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

Ответ 4

Проблема с итерацией each заключается в том, что она удаляет элемент из массива, заставляя пропустить итерацию. jQuery имеет хороший метод grep, который возвращает все элементы, соответствующие определенным критериям, которые определяются предоставленной анонимной функцией.

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

Рабочий пример http://jsfiddle.net/ktCEN/

Документация

Ответ 5

В качестве другой альтернативы вы можете просто уменьшить ваш индекс каждый раз, когда вы делаете splice. Например:

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

Этот параметр корректирует ваш индекс, чтобы поддерживать его достоверность при каждом изменении массива. Вы все еще можете использовать angular.forEach, и вы не получите две копии вашего массива.