This question already has answers here:
Deleting array elements in JavaScript - delete vs splice
(29 answers)
Closed 4 years ago.
I couldn't find a question that specifically targets the issue I'm having hence this question is being asked.
I have an array that holds 5 numbers:
var numbers = [0,1,2,3,4];
Once a number is clicked on the frontend (website), the number is removed from the array using the below code:
delete numbers[1];
This removes the correct number but leaves a space where the number was (the space is undefined). I believe this is causing an issue. After a number is removed from the array, I use another function to randomly pick any of the remaining numbers in the array however it sometimes fails. After much thought, I've realized it may be because there are empty spaces in the array after a number is removed and as a result, the code fails to work due to the undefined element.
Is my analogy correct or am I mistaken?
(I have also attempted to use the splice method to remove the number however that then causes an issue with the length of my array because if I later want to remove another number, it removes the wrong one due to the numbers moving around etc).
What you'd want to use is splice
In your specific case, numbers.splice(1,1)
You're correct that delete replaces one of the values in the array with undefined, and does not change the array length. Later on when you randomly choose an element from the array, you can wind up getting that undefined value, because it's still taking up a slot in the array:
var numbers = [0,1,2,3,4];
delete numbers[3];
console.log(numbers)
Instead use splice, which removes the item from the array completely:
var numbers = [0,1,2,3,4];
numbers.splice(3,1) /// remove one element starting at index 3
console.log(numbers)
if I later want to remove another number, it removes the wrong one due to the numbers moving around
You do need to choose one behavior or the other. If you need to preserve indexes as is, then continue to use delete, leaving the undefined values in the array, and rewrite your "choose one at random" function to never pick undefined values:
// start with some undefined values:
var numbers = [0, 1, undefined, undefined, undefined, 5]
var pickRandom = function(numbers) {
// make a copy of the array, removing undefined elements:
var definedValues = numbers.filter(function(item) {
return item !== undefined;
});
if (definedValues.length === 0) {return false}
//choose one at random:
return definedValues[Math.floor(Math.random() * definedValues.length)]
}
// test it:
console.log(pickRandom(numbers));
console.log(pickRandom(numbers));
console.log(pickRandom(numbers));
console.log(pickRandom(numbers));
(...but note that this suggests that a simple array is the wrong data structure to use here; you may be better off with an array of objects each with an explicit ID, so you can reference specific ones as needed without worrying about keeping the array index the same.)
If you mean to actually remove the element from the array, leaving your array with 4 elements, then you can use
numbers.splice(1);
This will remove the element in the index 1 from the array and return the section of the new array.
Related
This question already has answers here:
Filter and delete filtered elements in an array
(10 answers)
Closed 3 years ago.
I am iterating over an array of strings using forEach(), and testing each element's length to determine wether it is even or odd. If the length of the string is even, it will be removed using splice().
My input and output are shown below, and as you can see even though (i think) my conditions are correct, in my return array, I still get an even, two character word - which should have been spliced out.
Code:
function filterOddLengthWords(words) {
words.forEach(function(element, index) {
if (element.length%2===0) {
words.splice(index, 1);
}
})
return words;
}
var output = filterOddLengthWords(['there', 'it', 'is', 'now']);
console.log(output); // --> [ 'there', 'is', 'now' ]
I understand where the error is, but I just don't know how to compensate for it. I could possibly rewrite this by creating an empty array at the beginning of the function, and then testing each element against the inverse condition, using the push() method to add each positive to the empty array. However, that is more inefficient and I'm curious to see if my way is possible. Thanks for the answers in advance.
By splicing an array, you change the index, but forEach takes the elements in advance and the old indices.
Usually by using splice, the iteration is started from the end and if some item is remoce, the index remains for the items before.
You could filter the array.
function filterOddLengthWords(words) {
return words.filter(function(element) {
return element.length % 2;
});
}
var output = filterOddLengthWords(['there', 'it', 'is', 'now']);
console.log(output);
The problem with splicing the array in the forEach() is that when you splice() on the array at index 1 which has the element it the element at index 2 is is moved to index 1.
So in the next iteration when the index is 2 in the callback the element at the index 2 of the array is the value now instead of the element is. As now is odd it is kept but the element is is completely skipped.
Using Array.prototype.filter will work here as it does not modify the original array but instead collects the valid results into a new array.
I am getting some strange behaviour out of JavaScript array.length. In particular, I have an array that returns length as 3, when there is only one element in the array, at index 0. I've read this question/answer dealing with incorrect values returned by array.length, but it doesn't answer my question because my array doesn't seem to be associative.
Here's a screenshot of my console, demonstrating the odd behaviour.
The code is throwing an error because I'm looping over the first array using array.length and the code is trying to acccess the second and third elements it thinks should be in the array, and not finding them. Other entries in the database seem to not have this problem (see the second array in the screenshot).
So here's the question: Why is array.length in the first array 3 instead of 1?
The length Array property in javascript is writable from anyone and is not a reliable source to find the number of elements in the array.
Usually, it is safe to assume that the array has length elements in it, but sometime you can have different behaviours. This one is one of them.
var x = [1,2,3];
x.length = 5;
using a for construct will lead to some undefined values
for (var i = 0; i < x.length; i++) {
alert(x[i]);
}
using the forEach Array method would result (on Firefox at least) in the desired behaviour.
x.forEach(function(item) {
alert(item);
});
Note that, if you change the array length to a lesser value than the real value, the array would lose the extra elements and if you restore the original value the extra elements will be lost forever.
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length
i have an array that has one element and i want to use proarray.shift();
var proarray=[];
proarray[0]=1;
//...
proarray.shift();
//...
proarray[i]=5;
but when i do, it stops the program.
does it delete the array?
if it does, what should i do to prevent that? cuz i need that array for later.
and also i tried to use
var proarray=[];
proarray[0]=1;
//...
array.splice(0,1);
//...
proarray[i]=5;
but it didn't work.
what should i do?
From the docs.
The shift method removes the element at the zeroeth index and shifts
the values at consecutive indexes down, then returns the removed
value. If the length property is 0, undefined is returned.
The program is likely stopping because you are accessing an index in the array that no longer exists.
I'm practicing the array section of JavaScript Koan and I'm not fully understanding why these answers are correct. I added my assumptions below if someone could please clarify/let me know if I'm wrong :
it("should slice arrays", function () {
var array = ["peanut", "butter", "and", "jelly"];
expect(array.slice(3, 0)).toEqual([]);
Why wouldn't it at least slice "jelly" since the slice begins with
3? How does the cut off of 0 make it empty instead?
expect(array.slice(3, 100)).toEqual(["jelly"]);
If the cut off index goes beyond what currently exists in the array,
does this mean that a new array created from slice would contain all
indexes starting at 3 until the end of the array?
expect(array.slice(5, 1)).toEqual([undefined];
Will it always be undefined if the starting index doesn't exist in the
array?
});
The second argument to Array.slice() is the upper bound of the slice.
Think of it as array.slice(lowestIndex, highestIndex).
When you slice from index 3 to index 100, there is one item (in your case) that has index >= 3 and < 100, so you get an array with that one item. When you try to take a slice from index 3 to index 0, there can't be any items that meet the conditions index >= 3 and < 0, so you get an empty array.
--EDIT--
Also, array.slice() should never return undefined. That's one of the advantages of using it. If there are no matching values in the array, you just get back an empty array. Even if you say var a = new Array() and don't add any values to it, calling a.slice(0,1) will just give you an empty array back. Slicing from outside of the array bounds will just return an empty array also. a.slice(250) will return [] whereas a[250] will be undefined.
I’m 100% certain this code has been working before. Now it strangely doesn’t.
The goal is to create a multiple choice quiz for a flashcard. I create an array to store the card's ids: the first one goes the current card id, then three other random ones. My goal is to make sure they don’t repeat either the first card or themselves.
This is how I do it:
// Array of cards’ ids to use
var randomCardsIds = [];
// Get the active card’s element id, add it to the array
randomCardsIds[0] = this.activeCardId;
// Get the current cards collection
var allCurrentCards = this.carouselEl.items.items;
// Get three random ids of other cards
var i = 0
while (i<3) {
// Get a random card element
var randomCardEl = allCurrentCards[Math.floor(Math.random() * allCurrentCards.length)];
// Get its id
var randomCardElId = randomCardEl.body.down('.card').id;
randomCardElId = randomCardElId.substring(randomCardElId.indexOf('_')+1);
console.log(randomCardElId, randomCardsIds, randomCardsIds.indexOf(randomCardElId));
// Make sure it is not in the array yet, and then add it
if (randomCardsIds.indexOf(randomCardElId) == -1) {
randomCardsIds.push(randomCardElId);
i++;
}
// Otherwise, the loop will have to run again
}
Basically, in a loop, for each item I check whether it already exists in the array or not. If it doesn’t, push it to the array, otherwise, run the loop again. Here is the console logging result:
Well, the first thing: it always shows the final state of the array: as if it is already filled with the results, which is weird. But the most important thing: the script does not recognise a duplicate (e.g. in the first result, 74 is repeated, and in the second to last, 47).
It only returns something different then -1, when it finds a match in the second position (returns 1, obviously). When a match is in a different position in the array, it always returns -1.
What am I doing wrong here?
Are you testing this in IE6? The problem is indexOf doesnot work with IE6.
For alternative you can check Best way to find if an item is in a JavaScript array?
A variation on a shuffling algoruthm seems to be the best bet here.