I have a Real-Time web app, receiving push events from Node JS, adding to and removing from an array in Javascript. I have found after doing the typical splice to remove the item by the matching value's index, the size of the array decreases as expected, but sometimes the element isn't getting removed.
My theory is the real time data is adding to and removing from the array in parallel for different places in the array, and sometimes the index position becomes out of date at the moment in time it's about to do the splice.
How can I remove an item from an array by value in real time if the index is always changing?
for(var i=0; i<data.geometries.length; i++) {
if(data.geometries[i].id == item.id) { //Found Item in Array
//do some stuff
data.geometries.splice(i,1); //Remove Geometry
break;
}
}
You could use .filter method:
var result = data.geometries.filter(function (e) {
return e.id !== item.id;
});
What you need is a sparse array, where you can have an array with gaps or holes. Using splice to modify the array will affect any indexes that come after the removed element. So, you could use a regular array as a sparse array, but you would delete items differently. For example:
// array to act as sparse array
var set = [1,2,3,4];
// remove an item by value
var pos = set.indexOf(3);
if (pos !== -1) {
// don't 'splice', just set to undefined
set[pos] = undefined;
}
// add an item
if (set.indexOf(5) == -1) {
set.push(5);
}
// to get the values from sparse array
var values = set.filter(function(v) { return typeof v !== 'undefined' });
console.log(values);
// => [1, 2, 4, 5]
This way, your indexes stay the same across parallel processes and threads.
Related
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"])
I have an array of objects and I am removing elements based on object value, without knowing which element it is.
So... those lovely people who flagged my question as duplicate and suggest I read How do I remove a particular element from an array in JavaScript? are not helping.
My question... again...
I have an array of objects
I want to search and remove specific elements from the array. I have a solution that is long winded (loop thru the array once, make a list of element id's, then slice one by one in another loop). I am hoping there is another method (enhance my skillset too).
myarray.filter(function(item)
{
if( item.flag==="Y" )
{
// how to delete element from myarray
// delete implies remove it from myarray
}
});
All help appreciated =)
You have at least two options:
Create a new array with filter
The first is to create a new array containing only the entries you want. You're already on the right track with filter:
myArray = myArray.filter(function(item) {
return item.flag !== "Y";
});
The new array will only have the entries that the callback returned a truthy value for. In this case, only ones whose flag is !== "Y". Entries whose flag is === "Y" will be left out of it.
Note that if you have other references to this same array in other variables, they will continue to point to the old array. Only myArray (in the above) gets a reference to the new array.
Modify the existing array with splice and a loop
The second is to use a while loop and splice:
var n = 0;
while (n < myArray.length) {
if (myArray[n].flag === "Y") {
// Remove it
myArray.splice(n, 1);
} else {
// Move to next
++n;
}
}
Note we don't move to next if we remove one, because we need to test the one after it (which is now at the previous index).
Or as Nina points out in a comment, a for loop going backward:
for (var n = myArray.length - 1; n >= 0; --n) {
if (myArray[n].flag === "Y") {
// Remove it
myArray.splice(n, 1);
}
}
In that case, since we're modifying the array (not creating a new one), the changes will be visible through any reference to that same array, not just myArray. (Since they all point to the same object, which we modified the state of.)
Remove object in array by its property-value:
Remove value: 30:
arr.splice(arr.map(el => el.value).indexOf(30), 1);
DEMO
Remove name: "John":
arr.splice(arr.map(el => el.name).indexOf("John"), 1);
Suppose I've got a nested array:
// 3rd element in sub-array indicates a number of repeats
var list = [["a","b",1],["a","d",1],["a","b",1],["c","d",1]];
Task is to remove identical sub-arrays and increase number in single unique sub-array, which would indicate the number of repeats, so that above example would transform into smth like:
[["a","b",2],["a","d",1],["c","d",1]]
What would be the most efficient way to achieve this?
Currently I'm trying smth like this:
var list = new Array();
// Sort by second element
list.sort(function(a,b) {
return a[1] > b[1];
});
function collateList(element,index,array){
// if 1st&2nd element of subarray equals to 1st&2nd element of next subarray
if (array[index[0]]==array[index[0]+1] && array[index[1]]==array[index[1]+1]){
// increase 3rd element of subarray by 1
array[index[2]] = array[index[2]+1];
// remove next element from an array
array.splice((index+1),1);
}
}
list.forEach(collateList);
Let us first define the function determining if two subarrays are to be combined, in this case that their first two values are the same:
function match(e1, e2) { return e1[0]===e2[0] && e1[1]===e2[1]; }
Now let us define a function which finds a matching element in an array, based on a matching function, and returns its index. This is the same as Array.prototype.findIndex, were it defined.
function find(a, v, fn) {
for (i in a) { if (fn(v, a[i])) {return i;} }
return -1;
}
Now we feed the input through a reduce to create an new array with counts updated and duplicates removed:
list.reduce( // Boil down array into a result
function(result, elt) { // by taking each element
var prev = find(result, elt, match); // and looking for it in result so far.
if (prev !== -1) { // If found
result[prev][2]++; // increment previous occurrence;
} else { // otherwise
result.push(elt); // include as is in the result.
}
return result; // Use this result for next iteration.
},
[] // Start off with an empty array.
)
I found a solution to where I get returned an array of elements without duplicates:
Array1 = Array1.filter(function(val) {
return Array2.indexOf(val) == -1;
});
However, I want to modify this code just a little bit. Instead of being returned an array without duplicates, I want to do something when there is a duplicate. The problem is, I'm not sure how exactly this code works. The thing is I'm not sure how val gets set, or what it even is.
for (var i = 0; i < json.length; i++) {
var item = json[i];
// if json.indexOf(val?), do something
}
Read the docs for the Array filter method then. The val parameter of the callback will be passed the single array items, i.e. json[i] or item in your case:
for (var i = 0; i < json.length; i++) {
var item = json[i];
if (json.indexOf(item) >= 0) {
// do something
}
}
var newArray = array1.filter(function(v, i) {
return array1.indexOf(v) == i;
});
This will return only unique itesm from array1;
array1.filter(function(v, i) {
// write your code here ('v' is individual value and 'i' is its index)
// don't return any anything if you don't want unique array to be returned.
// 'array1.indexOf(v) == i' checks if current value is duplicate from previous any values.
// try putting console.log on values you don't understand like (console.log(v,i) for values of 'v' and 'i')
return array1.indexOf(v) == i;
});
and off-curse you can loop an array with for loop as
for(i in array1){
// where i is index of array1, to get current value use array1[i]
if(array2.indexOf(array1[i]) >= 0){
// do something
}
console.log(i);
}
val is set by Array.prototype.filter, which calls the callback function on each element in the array. Since you don't want to filter you can use Array.prototype.forEach instead, which also calls the callback function once for each element in the array:
Array1.forEach(
// This function is called once per element in Array1
function(val){
if(Array2.indexOf(val) != -1){ // Check if that element is also in Array2
// `val` is in both arrays,
// Do something with it
}
}
);
You can utilize some modern libraries... like underscorejs.
Intersection is what you're looking for i guess: http://underscorejs.org/#intersection
_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]
So your code may be something like
if(_.insersection(arr1, arr2)){
//since [] array is Falsy in JS this will work as a charm
}
From MDN: indexOf
Returns the first index at which a given element can be found in the array, or -1 if it is not present.
From MDN: filter
Creates a new array with all elements that pass the test implemented by the provided function.
The first function works by returning true when an item from array1 isn't found in array2 (== -1). i.e.: Iterate through A and add anything not found in B.
So, to change to return only duplicates return true for anything that is found in both:
Array1 = Array1.filter(function(val) {
return Array2.indexOf(val) >= 0;
});
Array1 now contains only items with duplicates.
I have an array of objects in javascript. I use jquery.
How do i get the first element in the array? I cant use the array index - as I assign each elements index when I am adding the objects to the array. So the indexes arent 0, 1, 2 etc.
Just need to get the first element of the array?
If you don't use sequentially numbered elements, you'll have to loop through until you hit the first one:
var firstIndex = 0;
while (firstIndex < myarray.length && myarray[firstIndex] === undefined) {
firstIndex++;
}
if (firstIndex < myarray.length) {
var firstElement = myarray[firstIndex];
} else {
// no elements.
}
or some equivalently silly construction. This gets you the first item's index, which you might or might not care about it.
If this is something you need to do often, you should keep a lookaside reference to the current first valid index, so this becomes an O(1) operation instead of O(n) every time. If you're frequently needing to iterate through a truly sparse array, consider another data structure, like keeping an object alongside it that back-maps ordinal results to indexes, or something that fits your data.
The filter method works with sparse arrays.
var first = array.filter(x => true)[0];
Have you considered:
function getFirstIndex(array){
var result;
if(array instanceof Array){
for(var i in array){
result = i;
break;
}
} else {
return null;
}
return result;
}
?
And as a way to get the last element in the array:
function getLastIndex(array){
var result;
if(array instanceof Array){
result = array.push("");
array.pop;
}
} else {
return null;
}
return result;
}
Neither of these uses jquery.
Object.keys(array)[0] returns the index (in String form) of the first element in the sparse array.
var array = [];
array[2] = true;
array[5] = undefined;
var keys = Object.keys(array); // => ["2", "5"]
var first = Number(keys[0]); // => 2
var last = Number(keys[keys.length - 1]); // => 5
I was also facing a similar problem and was surprised that no one has considered the following:
var testArray = [];
testArray [1245]= 31;
testArray[2045] = 45;
for(index in testArray){
console.log(index+','+testArray[index])
}
The above will produce
1245,31
2045,45
If needed you could exist after the first iteration if all that was required but generally we need to know where in the array to begin.
This is a proposal with ES5 method with Array#some.
The code gets the first nonsparse element and the index. The iteration stops immediately with returning true in the callback:
var a = [, , 22, 33],
value,
index;
a.some(function (v, i) {
value = v;
index = i;
return true;
});
console.log(index, value);
If you find yourself needing to do manipulation of arrays a lot, you might be interested in the Underscore library. It provides utility methods for manipulating arrays, for example compact:
var yourArray = [];
yourArray[10] = "foo";
var firstValue = _.compact(yourArray)[0];
However, it does sound like you are doing something strange when you are constructing your array. Perhaps Array.push would help you out?