Side effects when async operating on arrays - javascript

I'm learning node.js atm, now I'm asking myself:
How "threadsafe" are normal Arrays?
Example:
var myArr = ["Alpha", "Beta", "Gamma", "Delta"];
ee.on('event', function(itemString) {
//Loop over an Array that could change its length while looping through
for(var i=0; i<myArr.length; i++) {
// delete the item out of the array
if(myArr[i] == itemString)
myArr.splice(i,1);
}
});
If multiple of the Events are fired on the ee-Object, is there a chance, that the for Loop will fail because the indexes are already spliceed away?
Or said different: Is a way to ensure that the loop won't skip or fail because any elements that may be deleted by another callback call of the same event?
THX :)

node.js is single threaded and it does not interrupts sync execution.
Still, you're modifying the array while iterating it by its length which may lead to skipping elements.
Also, your event is not prepared to be fired twice for the same array element.

I think we've covered the threading issue well, but you really should still address the loop. For an example of the "skipping" problem I'm talking about, try this:
var a = [1, 2, 3, 4, 5];
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
if (a[i] === 2) {
a.splice(i, 1);
}
}
Output:
1
2
4
5
Notice how the number 3 is never even seen by this loop.
One common way to fix this kind of loop so you can safely delete elements of the array while iterating over it is to go backwards:
var a = [1, 2, 3, 4, 5];
for (var i = a.length - 1; i >= 0; i--) {
console.log(a[i]);
if (a[i] === 2) {
a.splice(i, 1);
}
}
Output:
5
4
3
2
1
Notice that we see all the elements of the array this way.

Related

Trying to understand why my function won't remove more than one instance of the value arguments[i] from newArr [duplicate]

This question already has answers here:
How to filter an array from all elements of another array
(24 answers)
Closed 8 months ago.
function destroyer(arr) {
const newArr = [...arguments[0]]
for(let i = 0; i < newArr.length; i++){
for(let j = 1; j < arguments.length; j++){
if(arguments[j] == newArr[i]){
newArr.splice(i,1)
console.log(newArr)
}
}
}
}
destroyer([3, 5, 1, 2, 2], 3, 5, 2);
New JS learner here.
Working on a problem that is supposed to look through the first arg in destroyer which will be an array and remove the elements that match the arguments following the array.
Results are [1,2] in the console output. Intended results are [1] with the given parameters Upon further testing it seems like the destroyer function is only removing the first instance of any value that it matches in newArr. If I take the second instance of '2' out of the test set it behaves as intended. I'm trying to understand what in my logic here is wrong. I've tried several different iteration patterns and can't seem to see what the problem is.
Thanks for any help!
I am going to link a few other answers here is this seems to be a fairly common question. The gist is that you are iterating over the live array while removing items from that array resulting in potentially skipping items.
How to iterate over an array and remove elements in JavaScript
Remove multiple elements from array in Javascript/jQuery
Alternatively you could also the filter method to help remove multiple values:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Try this:
function destroyer(arr) {
const newArr = [...arguments[0]]
for(let i = newArr.length - 1; i >= 0; i--){
for(let j = 1; j < arguments.length; j++){
if(arguments[j] == newArr[i]){
newArr.splice(i,1)
console.log(newArr)
}
}
}
}
This is an array pointer issue.
when i = 3 and j = 3, the function will match arguments[3] == newArr[3].
At this moment, newArr will be removed 1 element which is the first 2, and then the newArr becomes an new array that is [3,5,1,2].
The next index i is 4 which doesn't exist in the new newArr. So, the function will return and finish. That's why you get [3,5,1,2].
function destroyer (arg) {
if(!Array.isArray(arg)) return arg;
return arg.filter(x => !Array.prototype.slice.call(arguments, 1).includes(x))
}
It is because newArr is getting shorter as you splice it through your loops and the loop itself is also shortened.
see this probably your solution

Why does splice() behave weird?

I am trying to flatten multiple arrays into one and remove duplicate elements in the array for an exercise from FCC.
Specifically, the splice method shows one thing in the console but acts differently. Can anyone tell me why splice() is not deleting the the duplicates that I have identified with the nested loop?
function uniteUnique(arr) {
var arr1 = arguments;
newArr= [];
for(var i=0; i<arr1.length; i++){
for(var l=0; l<arr1[i].length; l++){
newArr.push(arr1[i][l]);
}
}
console.log(newArr);
for(var t=0; t<newArr.length; t++){
for(var p=0; p<newArr.length; p++){
if(newArr[t]===newArr[p+1]){
console.log("without splice ", newArr[p+1]);
console.log("with splice ", newArr.splice(newArr[p+1],1))
newArr.splice(newArr[p+1],1);
}
}
}
return newArr;
}
console.log(uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]));
Will output [4,1].
But should output [1,3,2,5,4]
first Iteration Log:
without splice 1
with splice [3]
Second:
without splice 1
with splice [2]
Third:
without splice 2
with splice [4]
Fourth:
without splice 1
with splice [2]
You have three errors here:
There's no need to use p+1 here. Just use p.
splice expects an index as its first argument, but you are passing the value being considered for removal as an index to perform the removal. This is doesn't make sense. Instead of .splice(newArr[p], 1) you need .splice(p, 1).
You do not stop a value from being considered against itself as a potential duplicate. Your if condition must also include the condition ... && p!=t since newArr[t]===newArr[p] will always be (uselessly) true in case that t equals p
Modifying an array in its loop is something is not recommended. If you don't have to use splice, just create a brand new array and push values by searching and if not already exists in it.
function uniteUnique() {
var newArr = [];
for(var i=0; i<arguments.length; i++) {
var arr = arguments[i];
for(var j=0; j<arr.length; j++) {
if(newArr.indexOf(arr[j]) == -1) {
newArr.push(arr[j]);
}
}
}
return newArr;
}
console.log(uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]));
In respect to the removing duplicates "in place" of your approach, splice is working fine and your algorithm is almost good, try to debug through each step of the iterations after the flattening, to remove the duplicates.
You are looping n squared times (because p = t and you iterate p * t times).
But, something is not doing what you want to, have a look at these:
if(newArr[t]===newArr[p+1]){ // HERE IT WON'T DO WHAT YOU WANT
I'm not going to give away the answer unless you mention it, I'm just trying to demonstrate that the problem does not rely with splice itself, rather than your algorithm.
The other line that you would need to modify would be this one
newArr.splice(newArr[p+1],1);
Basically with a few changes in these two lines, you would reach the goal to remove the duplicates.

When looping through values of a JS array, and I remove value, do I need to use while instead of for?

var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
for(var i = 0; i < myArray.length; i++){
if(isOdd(myArray[i])){
myArray.splice(i,1);
i--;
}
}
The code above takes an array of arbitrary length and checks each value. If the value of the bit of the array meets an arbitrary condition (in this case if it is odd), then it is removed from the array.
Array.prototype.splice() is used to remove the value from the array, and then i is decremented to account for the rest of the values in the array "moving down" to fill in the gap that the removed value left (so the loop doesn't skip over a value).
However, the for loop ends when i equals the length of the array, which gets shorter as values are removed.
Does the value of myArray.length decrease dynamically as the loop proceeds, or does it save the value at the start of the loop and not update as values are removed? If the latter, what can I do to fix my loop?
Thank you!
myArray.length is changing with the operation on the array. But looping and splicing leads to unwanted results, if not proper padded.
To prevent unnecessary corrections, use a while loop from the end, to keep the rest of the array for processing.
function isOdd(value) {
return value % 2;
}
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9],
i = myArray.length;
while (i--) {
if (isOdd(myArray[i])) {
myArray.splice(i, 1);
}
}
console.log(myArray);
The length property is read in every iteration, and the splice method does update its value, so it works as you would expect. However, I would say that this is not a good coding practice, a while loop is much more readable, so it should be the obvious choice.
To answer the question directly: you don't have to use while instead of for, but you definitely should.
Use Array.filter instead
var myArray = [1,2,3,4,5,6,7,8,9];
myArray=myArray.filter(function(item,index) {
return !(item % 2);
})
console.log(myArray)
This is where you'd want to use Array.prototype.filter() if you don't absolutely HAVE to modify the original array, in-place.
As you suspect, the .length property of the array is being updated every time you splice(). The filter() method was built for exactly this sort of operation.
var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
var filteredArray = myArray.filter(function(item){
return !isOdd(item);
});
console.log(filteredArray);
A more concise version of the above code:
var myArray = [1,2,3,4,5,6,7,8,9];
function isEven(value){
return value % 2 === 0;
}
var filteredArray = myArray.filter(isEven);
console.log(filteredArray);
An even more concise version relying on ES6 arrow syntax:
var myArray = [1,2,3,4,5,6,7,8,9];
var isEven = value => value % 2 === 0;
var filteredArray = myArray.filter(isEven);
console.log(filteredArray);
And, in the event that you absolutely MUST edit the array in-place / use splice() here, I would recommend using Array.prototype.forEach() over a for or while loop. forEach() is another higher order method that allows you to realize the same functionality with less boilerplate. As with most higher order methods/functions, it allows you to focus on defining what you need to do rather than exactly how it needs to be done.
var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
myArray.forEach(function(c, i, a){
if(isOdd(c)){
a.splice(i,1);
}
})
console.log(myArray);
You can use both of them and it's depends on which one you like. if you prefer to use while loop then Nina's answer looks good and if you want to use for loop then consider to manage counter changes by yourself completely or when the length changes:
function isOdd(value) {
return value % 2;
}
var arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr1.length;)
isOdd(arr1[i]) ? arr1.splice(i, 1) : i++;
console.log(arr1);
var arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr2.length; i++)
if (isOdd(arr2[i])) {
arr2.splice(i, 1);
i--;
}
console.log(arr2);

Javascript Arrays - Find Duplicates [duplicate]

This question already has answers here:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
(97 answers)
Closed 8 years ago.
Here is my question...
Given an array populated with numbers as a function parameter, produce a resulting array which contains any duplicates number from the array.
For example, given the array [ 1, 2, 4, 4, 3, 3, 1, 5, 3 ] it should return [1, 4, 3]. For extra bonus points return a sorted array.
I am starting out with Javascript - I know the language however, using it in the correct way ( as one should ) I'm still getting to grips with.
My pseudo code for this would be to:
Create an array with the numbers above var numbers = [1, 2, 4, 4, 3, 3, 1, 5, 3];
Then create an empty array named "result" var result = [];
Create a for loop that goes through the var numbers to check for duplicates which will then populate the empty array "result" with the duplicates
for (var i = 0;i < numbers.length; i++) {
//This is where I'm stuck...
}
I'm not sure what to do within the for loop to populate the var result and to throw in to the mix... The given array has to be a function parameter which makes sense so you can change the numbers in one place.
Any feedback on my thought process on this so far is greatly appreciated but ultimately I am wanting to learn how to achieve this.
Here is a JSFiddle of my progress so far... http://jsfiddle.net/fbauW/
One way of doing this (and it's not the only way) is by checking for existing elements in the array. Take a look at JavaScript's lastIndexOf function:
http://www.w3schools.com/jsref/jsref_lastindexof_array.asp
It will return -1 if the object does not exist in your array, and if it exists, will return an index of a later position than you are in. So you can use an if statement in your loop that checks whether or not there is another index containing your number, and add it in to your results array IF AND ONLY IF the index you get back != the index you are currently on (if they equal, this means that there is only one of that element in the list).
If you need more help, comment here and I can type some code in!
Good luck!
Array.prototype.contains = function(k) {
for ( var p in this)
if (this[p] === k)
return true;
return false;
};
//this prototype function checks if an element is already in the array or not
//go through all the array and push the element to result if it is not
//this way we can eliminate duplicates
//result will contain the resultant array
function findDuplicates(Numbers) {
var arrayLength = Numbers.length, i, j, result = [];
for (i = 0; i < arrayLength; i++) {
for (j = 0; j < arrayLength; j++) {
if (a[i] == a[j] && i != j && !result.contains(a[i])) {
result.push(a[i]);
}
}
}
return result;
}

I am pushing items into an array but the script becomes unresponsive?

I'm trying to add an array's members back to itself using a for loop.
Why does this code cause an unresponsive script?
var magicarray = {
arraymemeber: [1, 2, 3, 4, 5],
duplicate: function () {
for (var i = 0; i < this.arraymemeber.length; i++) {
this.arraymemeber.push(this.arraymemeber[i]);
};
}
};
console.log(magicarray.duplicate());
Though I'm not sure why you want this, to avoid an infinite loop that you currently have get the length first and iterate only over the original length of the array.
var magicarray = {
arraymemeber: [1,2,3,4,5],
duplicate: function() {
var length = this.arraymemeber.length;
for (var i = 0; i < length; i++) {
this.arraymemeber.push(this.arraymemeber[i]);
};
}
};
console.log(magicarray.duplicate());
Because you're pushing new items in as you iterate, and your condition is based on the current .length, causing an infinite iteration (or at least as high as the .length will be allowed to go).
If you wanted to "double" the Array, you don't need a loop. You can do this:
this.arraymember.push.apply(this.arraymember, this.arraymember)
So your object would be:
var magicarray = {
arraymemeber: [1, 2, 3, 4, 5],
duplicate: function () {
this.arraymember.push.apply(this.arraymember, this.arraymember)
}
};
Every time you go around the loop, you put an item onto the array. This increases its length by 1.
Since this.arraymemeber.length increases at the same rate as i, you never get to the end of the loop.
Because in each iteration you're adding a new element to the element you're iterating over.
The i < this.arraymemeber.length check is made at the end of each iteration. The length of the array isn't cached in any way.
To prevent an infinite loop, use
for (var i = 0, len = this.arraymemeber.length; i<len; i++) {
... instead.
Just do this:
this.arraymemeber = this.arraymemeber.concat(this.arraymemeber);

Categories

Resources