Remove item[i] from jQuery each loop - javascript

How do I remove an item[i] from items once it reaches in:
$.each(items, function(i) {
// how to remove this from items
});

It would be better not to use $.each in this case. Use $.grep instead. This loops through an array in pretty much the same way as $.each with one exception. If you return true from the callback, the element is retained. Otherwise, it is removed from the array.
Your code should look something like this:
items = $.grep(items, function (el, i) {
if (i === 5) { // or whatever
return false;
}
// do your normal code on el
return true; // keep the element in the array
});
One more note: this in the context of a $.grep callback is set to window, not to the array element.

I'm guessing you want $.map. You can return null to remove an item, and not worry about how indices might shift:
items = $.map(items, function (item, index) {
if (index < 10) return null; // Removes the first 10 elements;
return item;
});

If you want to remove an element from array, use splice()
var myArray =['a','b','c','d'];
var indexToRemove = 1;
// first argument below is the index to remove at,
//second argument is num of elements to remove
myArray.splice(indexToRemove , 1);
myArray will now contain ['a','c','d']

the solution is below:
_.each(data, function (item, queue) {
if (somecondition) {
delete data[queue]
}
});

Something like
var indexToBeRemoved = 3; // just an illustration
$.each(items, function(i) {
if(i==indexToBeRemoved){
$(this).remove();
}
});

As mentioned by #lonesomday above (I simply couldn't add this in a comment) grep is for Arrays, but you could insert your selector inside grep:
var items = $.grep($(".myselector", function (el, i) {
return (i===5) ? false : true;
};
This would store all elements found using $(".myselector")in ìtems` leaving out the item at the 6th position (the list is 0 indexed, which makes "5" the 6th element)

Although I would typically prefer using $.grep() to filter the array, I have an instance where I'm already using $.each() on the array to process a dataset. After doing some processing, I can determine whether or not the item needs to be removed from the array:
// WARNING - DON'T DO THIS:
$.each(someArray, function(index, item) {
// Some logic here, using 'item'
if (removeItem) {
// Spice this item from the array
someArray.splice(index, 1)
}
// More logic here
});
WARNING: This presents a new problem! Once the item has been spliced from the array, jQuery will still loop for the length of the original array. E.g.:
var foo = [1,2,3,4,5];
$.each(foo, function(i, item) {
console.log(i + ' -- ' + item);
if (i == 3){
foo.splice(i, 1);
}
});
Will output:
0 -- 1
1 -- 2
2 -- 3
3 -- 4
4 -- undefined
And foo is now [1, 2, 3, 5]. Every item in the array is "shifted" relative to the jQuery loop, and we missed the element "5" altogether, and the last item in the loop is undefined. The best way to solve this is to use a reverse for loop (going from arr.length - 1 to 0).
This will ensure that removing an element won't affect the next item in the loop. However since the question here is with respect to $.each, there are a few alternative ways of solving this:
1) $.grep() the array before looping
var someArray = $.grep(someArray, function(item) {
// Some logic here, using 'item'
return removeItem == false;
});
$.each(someArray, function(index, item) {
// More logic here
});
2) Push items into another array
var choiceArray = [ ];
$.each(someArray, function(index, item) {
// Some logic here, using 'item'
if (removeItem) {
// break out of this iteration and continue
return true;
}
// More logic here
// Push good items into the new array
choiceArray.push(item);
});

Related

Loop through multiple values and remove from array

I have an event that attempts to remove items from a list. Currently trying to use splice to remove them from the array of objects. I need to use a function that acutally uses the original array. As I want so see the change happen live in the frontend. The code below works to a certain extent, but only seems to remove some items per click.
Controller
$scope.clearAlerts = function(type) {
var array = [];
for (let item of $scope.notifications) {
if (item.read && type == 'seen') {
notifications.clearAlerts(item.watching_id);
//console.log(item.watching_id)
}
if (!item.read && type == 'notseen') {
notifications.clearAlerts(item.watching_id);
//console.log(item.watching_id) - This returns 8 items
}
}
}
Service
this.clearAlerts = function(watchingId = null) {
// Remove notifications
this.notifications.forEach((item, index, object) => {
if (item.watching_id === watchingId) {
//remove alerts from dom
object.splice(index, 1); - This only removes 2 or 3 per click
index--;
// update database
//return api.request('GET', 'notification/' + watchingId + '/clear', {watching_id: watchingId});
}
});
}
Any help on this would be much appreciated. Thanks
So if you were to use an object instead of an array where the keys of the object were say the ID you are watching then your service function should look something like this...
this.clearAlerts = function(watchingId = null) {
delete this.notifications[watchingId];
}
Much simpler, so the idea is that instead of using an array as in [object, object], us an object that is mapped out something like so
{
id1: object,
id2: object,
...
}
where id1, and id2 are actually taken from watching_id which you defined
As for the reason why your alerts aren't removed accurately is because you are mutating the array you are looping through.
Here is an example
const notifications = [
{
watching_id: 1
},
{
watching_id: 2
},
{
watching_id: 3
},
{
watching_id: 4
}
];
notifications.forEach((item,index, arr) => {
if (item.watching_id > 0) {
arr.splice(index, 1);
}
})
In this example at first sight it can seem that all items are going to be deleted, but that is not the case.
When the index is 0, you delete the first item, and after deletion you have an array with 3 items, so on the second iteration when the index is 1 you are deleting the item from 3 items array which index is 1 and that is the second item out of 3, the first item is intact.
As a solution you can add the third argument to the splice, in order to have the same length array, which will solve the problem. Decide yourself what is best for your specific case, 3rd argument can be anything null,'undefined' or an object object.splice(index, 1, {}).
By the way, index-- doesn't do anything, index is set on every iteration.

add item to array in loop by this array js

I have the following method:
var items = [1,2,3];
$.map(items, function (item) {
if (item === 1) {
items.push(4);
}
console.log(item);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
and I expect in console 1,2,3,4, but see 1,2,3. I mean I want to see one extra loop item.
Can I resolve it somehow? And if yes, how can I resolve it?
Iterator methods, like .map() or .forEach(), will prevent visiting elements added during iteration by using only the original length.
To avoid that, you'll want to use a standard loop, such as for..of (with the default array iterator checking length as it progresses):
var items = [1, 2, 3];
for (var item of items) {
if (item === 1) {
items.push(4);
}
console.log(item);
}
Though, other types of loops can be used to do the same.
Of course, beyond this current example, be careful that the loop doesn't become infinite from there always being new elements to iterate to next.
Yes certainly you can resolve it, but in your specific case, items is passed by value to your map function so that you won't accidentally alter the original variable. The purpose of map is not for what you are using, but for mapping by specific key for an object or associative array. You should fall back to for loop or some other method for getting your desired output.
var items = [1, 2, 3];
for (var i = 0; i < items.length; i++) {
const item = items[i];
if (item === 1) {
items.push(4);
}
console.log(item);
};
use forIn instead of forOf, because sometime forOf give an error (maybe forOf not supported older version of js )..
var items = [1,2,3];
var k;
for (k in items){
if(items[k] === 1){
items.push(4);
}
}
alert(items);

How to insert multiple items to an array at specific indexes inside a multi-dimensional array?

I have an array of arrays.
var arr = [[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7],[4,5,6,7,8],[5,6,7,8,9], [6,7,8,9,10], [7,8,9,10,11]];
I want to add a new item in front of and back of multiple items at specific indexes.
What I want to achieve is:
var new_arr = [["x",1,"x",2,3,4,5], [2,3,4,5,"x",6,"x"], [3,4,5,"x",6,"x",7], [4,5,"x",6,"x",7,8], [5,"x",6,"x",7,8,9], ["x",6,"x",7,8,9,10], [7,8,9,10,11]];
The issue is, when I use splice to insert a new item inside the iterated arrays, indexes does change. Because splice is a destructive function.
Here is what I tried:
var result = [];
_.each(arr, function(item, index) {
index_a = item.indexOf(1);
index_b = item.indexOf(6);
var temp_item = item.slice(0);
if(~index_a || ~index_b) {
temp_item.splice(index, 0, "x");
temp_item.splice(index + 2, 0, "x");
if(index_b > -1) {
temp_item.splice(index, 0, "x");
}
}
result.push(item);
}
During the iteration above, the first splice works just fine. But the second "x" is not placed properly. I think the reason is first splices' effect on the temp_item array. Because number of items in the array is changing.
So how can I achieve what I want? Thank you.
Can you just add the second one first? That won't change the index of the first.
I think this does what you want. Splicing the higher index first maintains lower index posiitoning
function padArr(val, arr){
arr.forEach(function(subArr){
var idx = subArr.indexOf(val);
if( idx >-1){
subArr.splice(idx+1,0,'x' );
subArr.splice(idx,0,'x')
}
});
return arr;
}
// usage
arr = padArr(1, arr);
arr = padArr(6, arr);
DEMO
I changed a few things, but its working. Oriol is correct the indexOfs you had would always be -1.
In my solution I map over the matrix and evaluate if each row contains 1 or 6, if it does cache the element by index and splice the index with "x",elm"x"
var matrix = [
[1,2,3,4,5],
[2,3,4,5,6],
[3,4,5,6,7],
[4,5,6,7,8],
[5,6,7,8,9],
[6,7,8,9,10],
[7,8,9,10,11]
];
function wrapElementWithX(arr, index) {
// cache the element
var elm = arr[index];
// splice with the "x", element, "x"
arr.splice(index, 1, 'x', elm, 'x');
}
matrix.map(function (row) {
var indexOf1 = row.indexOf(1);
var indexOf6 = row.indexOf(6);
// if the row has `1`
// wrap
if (~indexOf1) {
wrapElementWithX(row, indexOf1);
}
// if the row has `6`
// wrap
if (~indexOf6) {
wrapElementWithX(row, indexOf6);
}
return row;
});
also go a jsfiddle example

How can I implement the lodash _.remove function in modern browsers?

I have this code using lodash:
_.remove(this.home.modal.data.subTopics, function (currentObject) {
return currentObject.subTopicId === subTopicToDelete;
});
Can someone give me advice as to how I could do the same using modern browser functions without lodash?
Note it would be okay for the output of the remove to go into another variable.
You could use Array#filter() and negate the filter clause:
this.home.modal.data.subTopics.filter(function (currentObject) {
return currentObject.subTopicId !== subTopicToDelete;
});
This will return an array where subTopicId does not equal subTopicToDelete. It's then up to you to save it in a variable or wherever.
Or, if you want to create a method out of it, you could do:
function remove(array, filterMethod) {
return array.filter(function(){
return !filterMethod.apply(this, arguments);
});
}
Why not have a look at lodash's source code for _.remove?
function remove(array, predicate, thisArg) {
var index = -1,
length = array ? array.length : 0,
result = [];
predicate = getCallback(predicate, thisArg, 3);
while (++index < length) {
var value = array[index];
if (predicate(value, index, array)) {
result.push(value);
splice.call(array, index--, 1);
length--;
}
}
return result;
}
(The getCallback call is not really interesting here, just replace it with a predicate function that returns a boolean value for the given arguments: value, index, array. Not all of them need to be supplied obviously, this is JavaScript after all!)
Lodash uses Array.prototype.splice at the appropriate position, pushing the removed element onto the result array. Then it decreases the current loop index and the saved length by 1 using --, because every time you use .splice, you modify the array directly, for instance:
var arr = ['a', 'b'];
arr.splice(0, 1);
arr[1] // undefined
splice in this context really just the same as Array.prototype.splice. You can as well do array.splice(index--, 1).
A maybe more simple/understandable way is to (for-)loop through the array from the right, starting at array.length - 1 and ending at 0. Then splice every element at the current index, if it passes the predicate function, and push the result value of that operation onto the result array. Return the result array after the loop.
This works the same, because if you start removing elements from the right side, the index of the rest of the looped elements doesn't change. Maybe there are performance advantages to lo-dash's code, but I couldn't tell you that.
You could adapt Array.prototype to suit your needs. Some people don't like this approach, but it can be useful sometimes. In this example I pass in the key and the value I want to amend the array by:
if (!Array.prototype.remove) {
Array.prototype.remove = function (key, value) {
return this.filter(function (el) {
return el[key] !== value;
});
}
}
data.remove('name', 'dan');
DEMO

Loop to remove an element in array with multiple occurrences

I want to remove an element in an array with multiple occurrences with a function.
var array=["hello","hello","world",1,"world"];
function removeItem(item){
for(i in array){
if(array[i]==item) array.splice(i,1);
}
}
removeItem("world");
//Return hello,hello,1
removeItem("hello");
//Return hello,world,1,world
This loop doesn't remove the element when it repeats twice in sequence, only removes one of them.
Why?
You have a built in function called filter that filters an array based on a predicate (a condition).
It doesn't alter the original array but returns a new filtered one.
var array=["hello","hello","world",1,"world"];
var filtered = array.filter(function(element) {
return element !== "hello";
}); // filtered contains no occurrences of hello
You can extract it to a function:
function without(array, what){
return array.filter(function(element){
return element !== what;
});
}
However, the original filter seems expressive enough.
Here is a link to its documentation
Your original function has a few issues:
It iterates the array using a for... in loop which has no guarantee on the iteration order. Also, don't use it to iterate through arrays - prefer a normal for... loop or a .forEach
You're iterating an array with an off-by-one error so you're skipping on the next item since you're both removing the element and progressing the array.
That is because the for-loop goes to the next item after the occurrence is deleted, thereby skipping the item directly after that one.
For example, lets assume item1 needs to be deleted in this array (note that <- is the index of the loop):
item1 (<-), item2, item3
after deleting:
item2 (<-), item3
and after index is updated (as the loop was finished)
item2, item3 (<-)
So you can see item2 is skipped and thus not checked!
Therefore you'd need to compensate for this by manually reducing the index by 1, as shown here:
function removeItem(item){
for(var i = 0; i < array.length; i++){
if(array[i]==item) {
array.splice(i,1);
i--; // Prevent skipping an item
}
}
}
Instead of using this for-loop, you can use more 'modern' methods to filter out unwanted items as shown in the other answer by Benjamin.
None of these answers are very optimal. The accepted answer with the filter will result in a new instance of an array. The answer with the second most votes, the for loop that takes a step back on every splice, is unnecessarily complex.
If you want to do the for loop loop approach, just count backward down to 0.
for (var i = array.length - 0; i >= 0; i--) {
if (array[i] === item) {
array.splice(i, 1);
}
}
However, I've used a surprisingly fast method with a while loop and indexOf:
var itemIndex = 0;
while ((itemIndex = valuesArray.indexOf(findItem, itemIndex)) > -1) {
valuesArray.splice(itemIndex, 1);
}
What makes this method not repetitive is that after the any removal, the next search will start at the index of the next element after the removed item. That's because you can pass a starting index into indexOf as the second parameter.
In a jsPerf test case comparing the two above methods and the accepted filter method, the indexOf routinely finished first on Firefox and Chrome, and was second on IE. The filter method was always slower by a wide margin.
Conclusion: Either reverse for loop are a while with indexOf are currently the best methods I can find to remove multiple instances of the same element from an array. Using filter creates a new array and is slower so I would avoid that.
You can use loadash or underscore js in this case
if arr is an array you can remove duplicates by:
var arr = [2,3,4,4,5,5];
arr = _.uniq(arr);
Try to run your code "manually" -
The "hello" are following each other. you remove the first, your array shrinks in one item, and now the index you have follow the next item.
removing "hello""
Start Loop. i=0, array=["hello","hello","world",1,"world"] i is pointing to "hello"
remove first item, i=0 array=["hello","world",1,"world"]
next loop, i=1, array=["hello","world",1,"world"]. second "hello" will not be removed.
Lets look at "world" =
i=2, is pointing to "world" (remove). on next loop the array is:
["hello","hello",1,"world"] and i=3. here went the second "world".
what do you wish to happen? do you want to remove all instances of the item? or only the first one? for first case, the remove should be in
while (array[i] == item) array.splice(i,1);
for second case - return as soon as you had removed item.
Create a set given an array, the original array is unmodified
Demo on Fiddle
var array=["hello","hello","world",1,"world"];
function removeDups(items) {
var i,
setObj = {},
setArray = [];
for (i = 0; i < items.length; i += 1) {
if (!setObj.hasOwnProperty(items[i])) {
setArray.push(items[i]);
setObj[items[i]] = true;
}
}
return setArray;
}
console.log(removeDups(array)); // ["hello", "world", 1]
I must say that my approach does not make use of splice feature and you need another array for this solution as well.
First of all, I guess your way of looping an array is not the right. You are using for in loops which are for objects, not arrays. You'd better use $.each in case you are using jQuery or Array.prototype.forEach if you are using vanila Javascript.
Second, why not creating a new empty array, looping through it and adding only the unique elements to the new array, like this:
FIRST APPROACH (jQuery):
var newArray = [];
$.each(array, function(i, element) {
if ($.inArray(element, newArray) === -1) {
newArray.push(region);
}
});
SECOND APPROACH (Vanila Javascript):
var newArray = [];
array.forEach(function(i, element) {
if (newArray.indexOf(element) === -1) {
newArray.push(region);
}
});
I needed a slight variation of this, the ability to remove 'n' occurrences of an item from an array, so I modified #Veger's answer as:
function removeArrayItemNTimes(arr,toRemove,times){
times = times || 10;
for(var i = 0; i < arr.length; i++){
if(arr[i]==toRemove) {
arr.splice(i,1);
i--; // Prevent skipping an item
times--;
if (times<=0) break;
}
}
return arr;
}
An alternate approach would be to sort the array and then playing around with the indexes of the values.
function(arr) {
var sortedArray = arr.sort();
//In case of numbers, you can use arr.sort(function(a,b) {return a - b;})
for (var i = 0; sortedArray.length; i++) {
if (sortedArray.indexOf(sortedArray[i]) === sortedArray.lastIndexOf(sortedArray[i]))
continue;
else
sortedArray.splice(sortedArray.indexOf(sortedArray[i]), (sortedArray.lastIndexOf(sortedArray[i]) - sortedArray.indexOf(sortedArray[i])));
}
}
You can use the following piece of code to remove multiple occurrences of value val in array arr.
while(arr.indexOf(val)!=-1){
arr.splice(arr.indexOf(val), 1);
}
I thinks this code much simpler to understand and no need to pass manually each element that what we want to remove
ES6 syntax makes our life so simpler, try it out
const removeOccurences = (array)=>{
const newArray= array.filter((e, i ,ar) => !(array.filter((e, i ,ar)=> i !== ar.indexOf(e)).includes(e)))
console.log(newArray) // output [1]
}
removeOccurences(["hello","hello","world",1,"world"])

Categories

Resources