let todos = [
{"id": 0, "completed": true, "name": "done :)"},
{"id": 1, "completed": true, "name": "done-skippedThisIndex :("},
{"id": 2, "completed": false, "name": "working on it"},
{"id": 3, "completed": false, "name": "incomplete"},
{"id": 4, "completed": true, "name": "finished"},
{"id": 5, "completed": true, "name": "complete-skippedThisIndex :("},
{"id": 6, "completed": true, "name": "complete-neverSeesThisIndex :)"},
{"id": 7, "completed": true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);
todos = todos.filter(function(todo, i){
console.log(i, "of", todos.length, todo);
return (todo.completed === false);
});
console.log("AFTER:(length:", todos.length, ")\n", todos);
Примечание: я только передал i
, поэтому console.log мог показать, что все элементы массива были обработаны для сравнения с кодом в следующем примере, что эквивалентно к вашему коду.
На самом деле вам нужен только следующий код:
todos = todos.filter(function(todo){
return (todo.completed === false);
});
С синтаксисом ES6 это может быть сокращено еще больше:
todos = todos.filter(todo => todo.completed === false);
Вот эквивалент вашего кода. Если вы изучите вывод console.log
, то увидите, что при удалении элементов из массива i
больше не соответствует номеру id
. Это связано с тем, что по мере продвижения элемента в массиве значение, на которое указывает i
, изменяется. В результате то, что было бы следующим элементом, на который смотрел ваш массив, теперь занимает пространственное местоположение текущего i
. Теперь при следующем проходе l oop, i
приращения и элемент, который двигался вперед, таким образом, никогда не просматривается. Это приводит к тому, что некоторые элементы «пропускаются».
let todos = [
{"id": 0, "completed": true, "name": "done :)"},
{"id": 1, "completed": true, "name": "done-skippedThisIndex :("},
{"id": 2, "completed": false, "name": "working on it"},
{"id": 3, "completed": false, "name": "incomplete"},
{"id": 4, "completed": true, "name": "finished"},
{"id": 5, "completed": true, "name": "complete-skippedThisIndex :("},
{"id": 6, "completed": true, "name": "complete-neverSeesThisIndex :)"},
{"id": 7, "completed": true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);
for (var i = 0; i < todos.length; i++) {
if (todos[i].completed === true) {
console.log(i,"of", todos.length, ": REMOVING", todos[i]);
todos.splice(i, 1);
console.log(" ", todos.length, "(new length - next id will be skipped over)\n");
} else {
console.log(i,"of", todos.length, ": keeping", todos[i]);
console.log(" ", todos.length, "(same length)\n");
}
}
console.log("AFTER:(length:", todos.length, ")\n", todos);
Чтобы сохранить созданную вами циклическую конструкцию, просто измените свой код, чтобы не изменять массив во время его цикла. Вы можете создать новый массив, который вы будете заполнять элементами, которые хотите сохранить. Затем переназначьте новый массив старой переменной:
let todos = [
{"id": 0, "completed": true, "name": "done :)"},
{"id": 1, "completed": true, "name": "done-skippedThisIndex :("},
{"id": 2, "completed": false, "name": "working on it"},
{"id": 3, "completed": false, "name": "incomplete"},
{"id": 4, "completed": true, "name": "finished"},
{"id": 5, "completed": true, "name": "complete-skippedThisIndex :("},
{"id": 6, "completed": true, "name": "complete-neverSeesThisIndex :)"},
{"id": 7, "completed": true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);
newTodos = [];
for (var i = 0; i < todos.length; i++) {
if (todos[i].completed === false) {
console.log(i,"of", todos.length, ": keeping", todos[i]);
newTodos.push(todos[i]);
}
else {
console.log(i,"of", todos.length, ": NOT keeping", todos[i]);
}
}
todos = newTodos;
console.log("AFTER:(length:", todos.length, ")\n", todos);
Здесь вы можете видеть, что все элементы фактически просматриваются, индекс не пропускается.
В результате получаются именно те элементы, которые вам нужны.
Другой «хакерский», НЕ рекомендуемый способ, который вы могли бы использовать, - противодействовать эффекту изменения индекса следующего элемента путем уменьшения текущего индекса, когда вы splice
выводите текущий элемент. Таким образом, в начале l oop вы должны увеличить i
, чтобы он указывал на следующий элемент, на который вы хотите посмотреть:
let todos = [
{"id": 0, "completed": true, "name": "done :)"},
{"id": 1, "completed": true, "name": "done-skippedThisIndex :("},
{"id": 2, "completed": false, "name": "working on it"},
{"id": 3, "completed": false, "name": "incomplete"},
{"id": 4, "completed": true, "name": "finished"},
{"id": 5, "completed": true, "name": "complete-skippedThisIndex :("},
{"id": 6, "completed": true, "name": "complete-neverSeesThisIndex :)"},
{"id": 7, "completed": true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);
for (var i = 0; i < todos.length; i++) {
if (todos[i].completed === true) {
console.log(i,"of", todos.length, ": REMOVING", todos[i]);
todos.splice(i, 1);
i--; // decrement i to reflect that the data moved backwards in the array
console.log(" ", todos.length, "(new length)\n ", i, "(new i) decremented i to reflect that the data moved backwards/(to the left by 1) in the array. \n Now, all items will still be looked at)\n");
} else {
console.log(i,"of", todos.length, ": keeping", todos[i]);
console.log(" ", todos.length, "(same length)\n");
}
}
console.log("AFTER:(length:", todos.length, ")\n", todos);
Это достигается путем добавления i--
сразу после выполнения splice(i, 1)
, чтобы отразить, что оставшиеся данные теперь расположены ровно на 1 позицию слева от того места, где они были ранее. , Это гарантирует, что все данные будут просмотрены и оценены. Это не рекомендуемый способ справиться с этой ситуацией, но может пригодиться в других сложных ситуациях.
for (var i = 0; i < todos.length; i++) {
if (todos[i].completed === true) {
todos.splice(i, 1);
i--; // all the data moved left, update the index accordingly
}
}