Array.splice inside a for loop causing errors - javascript

im using Angular ng-repeat to display $scope.currentMessageList array
i also have a remove button bound via ng-click to the remove function, which looks like this:
remove: function () {
for (var i = 0; i < 25; i++) {
var index = i;
$scope.currentMessageList.splice(index, 1);
console.log($scope.currentMessageList.length + 'left');
}
}
There are 25 items in this collection, when I call the remove function,
I get this output:
24left
23left
22left
21left
20left
19left
18left
17left
16left
15left
14left
13left
13times X 12left
If I replace the for loop with angular.forEach
I get "12 left" only once, still it doesn`t remove more than 13 items
Ive also tried to use angular.apply, than I get digest already in progress error

Performing a splice while iterating through an array is a bad idea.
You should replace
for( var i = 0; i < 25; i++ ){
var index = i;
$scope.currentMessageList.splice( index, 1 );
console.log($scope.currentMessageList.length + 'left');
}
by a simple
$scope.currentMessageList.splice( 0, 25 );

You're removing items while walking the array.
When you reach half of the array you've already removed half the items, so you won't remove anything else.
You can fix this either by always removing the first item or by iterating backwards from 24 towards 0.

When you remove array items in loop, indexes get shifted too. As the result you can iterate over only the half of them. This is the issue here.
If you want to clear 25 first items you can remove them with Array.prototype.shift method instead. In this case it will remove the first element of the array 25 times, giving you expected result:
remove: function () {
for (var i = 0; i < 25; i++) {
currentMessageList.shift();
}
}

When you splice the array.. the length of the array changes.
When you are trying to remove the element at index 13, the length is 12 only.
Hence it is not removed.
Instead of splice, try shift();

You don't need to iterate over your array to remove all the items. Just do this:
remove : function(){
$scope.currentMessageList = [];
}
Check out this answer also. There are others way to achieve this that are also valid.

Related

how can I pop all array list

ı couldn't pop() every array list. at the end remain two array elements
function ürünSil(){
let diller = [ "Türkçe", "İngilizce", "Almanca", "Fransızca", "Japonca"]
for(let i in diller){
let sonİcerik = diller.pop()
document.write(diller + "<br />")
}
}
you can empty your array like this also:
let diller = [ "Türkçe", "İngilizce", "Almanca", "Fransızca", "Japonca"];
while(diller.length > 0) {
diller.pop();
}
console.log(diller)
The length of the array changes each time on pop() so when there are 3 items removed from the array , the present index for i on your code will be 2 and hence the actual length is also 2, so the for() loops does not trigger further.
The solution could be to preserve the initial length value and use that value in the loop:
function ürünSil(){
let diller = [ "Türkçe", "İngilizce", "Almanca", "Fransızca", "Japonca"]
const length = diller.length;
for(let i = 0; i<length; i++){
let sonİcerik = diller.pop()
console.log(sonİcerik);
}
}
ürünSil()
use while loop instead of for.
while (diller.length != 0) {
diller.pop();
document.write(diller + '<br />');
}
This happens because pop reduces the length of the array, which impacts the times the loop will iterate. Instead just have the while condition check whether the array still has elements, without any i.
Unrelated, but
don't use document.write for this. Use console.log, or if you want to add data to the HTML document, then use other DOM methods.
have the good habit of ending your statements with semicolon. You don't want to fall for the sometimes tricky effects of automatic semicolon insertion.
let diller = [ "Türkçe", "İngilizce", "Almanca", "Fransızca", "Japonca"];
while (diller.length) {
let sonİcerik = diller.pop();
console.log(sonİcerik);
}

How to sort pushed array by id?

I have two arrays. First one which comes from response is divided by pagination. When i remove items from second array, they should be placed back sorted by id in first array, instead they go to bottom of array and i have to scroll down to find certain element. This is my code for pushing elements from array vm.feeds to vm.rationList:
function addAll() {
var mList = JSON.parse(JSON.stringify(vm.feeds))
for (var i = 0; i < mList.length; i++) {
mList[i].is_selected = false;
vm.rationList.push(mList[i]);
}
vm.feeds = [];
vm.rationListSafe = vm.rationList;
if(vm.feeds.length == 0){
vm.currentPageMaster++;
vm.isPage = true;
vm.disableScroll = true;
getFeedsByTeam(vm.selectedTeam);
}
}
Second part is how i remove elements from array vm.rationList and push them back to first array - vm.feeds:
function removeAll() {
for (var i = 0; i < vm.rationList.length; i++) {
vm.feeds.push(vm.rationList[i])
}
vm.rationList = []
}
In removeAll() i have to add additional check which will sort them by ID on push. Any idea for this?
The Array.push method add item to the end of your list.
To put an element at a given position in an array, you may prefer using Array.splice.
First argument is the index, second is the number of items to delete (in your case, 0), and a third argument would be the item to add at the given index.
With that, you can replace the push in removeAll by
vm.feeds.splice(correctIndex, 0, vm.rationList[i]);
Question is : what is correctIndex ?
Well, for that, there might be many solutions depending on your architecture, but a simple one could be to loop on vm.feeds to find the first item with an id that is not lower than the one you want to add:
var correctIndex = 0;
for(var item of vm.feeds) {
if (item.id >= vm.rationList[i].id) {break;}
else {correctIndex++;}
}
vm.feeds.sort(function(a, b) {
return parseFloat(a.id) - parseFloat(b.id);
});
This is the solution I found and it works

Using Jquery Splice

I am trying to remove an item from an Array using Splice method.
arrayFinalChartData =[{"id":"rootDiv","Project":"My Project","parentid":"origin"},{"1":"2","id":"e21c586d-654f-4308-8636-103e19c4d0bb","parentid":"rootDiv"},{"3":"4","id":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02","parentid":"e21c586d-654f-4308-8636-103e19c4d0bb"},{"5":"6","id":"b8d2598a-2384-407a-e2c2-8ae56c3e47a2","parentid":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02"}];
ajax_delete_id = "e21c586d-654f-4308-8636-103e19c4d0bb,deca843f-9a72-46d8-aa85-f5c3c1a1cd02,b8d2598a-2384-407a-e2c2-8ae56c3e47a2";
$.each(arrayFinalChartData, function (idx, obj) {
var myObj = obj.id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
var vararrayFinalChartDataOne = arrayFinalChartData.splice(idx, 1);
}
});
console.log(arrayFinalChartData);
Please check at : http://jsbin.com/deqix/3/edit
Note : It does not complete the "last leg " of the loop. That means if I have 4 items, then it successfully executes 3 items. Same goes for 6,7...items.
I need to "REMOVE" few items and "PRESERVE THE BALANCE" in an array.
You can use for loop instead of $.each function:
alert('length before delete ' + arrayFinalChartData.length);
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
id = arrayFinalChartData[i].id;
if(ajax_delete_id.indexOf(id) > -1){
arrayFinalChartData.splice(i, 1);
}
};
alert('length after delete ' + arrayFinalChartData.length);
Demo.
Complete edit :
After researching a bit, and console.logging a lot, I finally found where the issue is coming from ! It's actually quite simple, but very sneaky !
Theoretical explanation :
You are calling the splice function with the variable "idx", but remember that the splice function remaps / reindexes your array ! So, each time you splice the array, its size decreases by one while you're still inside the $.each function. The splice messes up jQuery indexation of your array, because jQuery doesn't know that you're removing elements from it !
Iterative explanation :
$.each function starts, thinking your array has 4 elements, which is true, but only for a while. First loop, idx = 0, no splice. Second loop, idx = 1, splice, which means that your array has now 3 elements left in it, reindexed from 0 to 2. Third loop, idx = 2, splice, which means your array has now two elements left in it, but $.each continues ! Fourth loop, idx = 3, js crashes, because "arrayFinalChartData[3]" is undefined, since it was moved back each time the array got spliced.
To solve your problem, you need to use a for loop and to start analyzing the array from the end, not from the beginning, hence each time you splice it, your index will decrease as well. And if you want to preserve balance, just push the removed items into an array. Remember that you are analyzing the array from the end, so items pushed into the "removedItems" array will be in reverse order. Just like this :
var removedItems = new Array();
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
removedItems.push(arrayFinalChartData.splice(i, 1)[0]);
}
}
console.log(arrayFinalChartData);
console.log(removedItems);
And a working demo (inspect the page, observe the console and click "Run") :
http://jsfiddle.net/3mL6C/3/
I will not give credit to myself for this answer, thanks to another similar thread for giving me a hint.
Your problem here is that when the $.each is set up, it's expecting a certain length of object, which you are then changing. You need to loop in a way that respects the dynamic length of the object.
var i = 0;
while (i < arrayFinalChartData.length) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
// current item is in the list, so remove it but KEEP THE SAME INDEX
arrayFinalChartData.splice(i, 1);
} else {
// item NOT in list, so MOVE TO NEXT INDEX
i++
}
}
console.log(arrayFinalChartData);
Demo

Unsetting javascript array in a loop?

I have the following code:
$.each(current, function(index, value) {
thisArray = JSON.parse(value);
if (thisArray.ttl + thisArray.now > now()) {
banned.push(thisArray.foodID);
}
else{
current.splice(index,1);
}
});
There's a problem with the following line:
current.splice(index,1);
What happens is that it (probably) unsets the first case which fits the else condition and then when it has to happen again, the keys don't match anymore and it cannot unset anything else. It works once, but not in the following iterations.
Is there a fix for this?
You can use a regular for loop, and loop backwards:
for(var i=current.length-1; i>= 0; i--) {
var thisArray = JSON.parse(current[i]);
if (thisArray.ttl + thisArray.now > now()) {
banned.push(thisArray.foodID);
} else {
current.splice(i, 1);
}
});
Also it seems thisArray is actually an object, not an array...
you can use regular for loop, but after splice, decrease index by 1
for(var i=0; i<current.length; i++){
current.splice(i, 1); i--;
}
You should pay attention if you mutate an array (by moving objects) while it's being traversed because some element may get skipped like in your case.
When you call current.splice(index, 1) element index will be removed and element index+1 will take its place. But then index will be incremented, thus skipping one element.
A better solution is IMO the read-write approach. You keep one index as the "read pointer" and you always increment it, and another index (the "write pointer") that is incremented only when you decide an element should be kept in the array:
var wp = 0; // The "write pointer"
for (var rp=0; rp<a.length; rp++) {
if (... i want to keep element a[rp] ...) {
a[wp++] = a[rp];
}
}
a.splice(wp); // Remove all elements after wp
This is a o(N) operation and will move each element at most once. Other approaches like starting from the end and using the same splice(i, 1) approach instead will keep moving all elements after i each time an element needs to be removed.

Why is the JS Array skipping certain entries when running the splice method within a loop?

I have an array:
var productIds = new Array("1","6","7","Product-Total","ccFirst","ccLast","email","ccExpMonth","ccExpYear","billingAddress","billingCity","billingState","billingZip");
I want to delete a value if it is not a number:
for(var i=0; i<productIds.length; i++){
if(isNaN(Number(productIds[i]))) {
productIds.splice(i,1);
}
}
It seems the splice method is affecting the positions of the values.
I found this solution(Looping through array and removing items, without breaking for loop) which is what I think I need, but I can't figure out how to implement their answers for my code.
How can I fix my problem?
btw, I posted a more detailed jsFiddle: http://jsfiddle.net/fte3m/2/
When you delete the entry at index i, you need to subtract 1 from i so the entry that was moved down is not skipped.
for(var i=0; i<productIds.length; i++){
if(isNaN(productIds[i])) {
productIds.splice(i--,1); // <-- Decrement i
}
}
As RobG points out in his comment, it's easier to simply process the array in the other direction:
for(var i=productIds.length - 1; i>=0; i--){
if(isNaN(productIds[i])) {
productIds.splice(i,1);
}
}
Alternatively, if you don't mind reassigning productIds to be a new array object and you are running JS 1.6 or later:
productIds = productIds.filter(function (id) {
return !isNaN(id);
});
(Note that in the above, I just use isNaN(value) rather than isNaN(Number(value)). Whenever Number(value) would return NaN, isNaN(value) will return true, and vice versa. Note also that neither approach will filter out a null id, since Number(null)==0 and isNaN(null)==false. If you want to exclude null entries from the result, you will need to test for that separately.)

Categories