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.
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.
this.arol.filter(x=>x.length!==0
?(this.arol.splice(this.arol.indexOf(x),1))
:!true)
I was trying to change it many different ways, but it still does not delete all elements of the array, it always leaves 1 or 2 behind deleting most of them.... I think the problem is with the condition... We are checking if the length of array elements is not 0 (which are all strings)...
Don't try to splice while filtering - instead, return from the filter callback a truthy or falsey value, depending on whether you want to include the item being iterated over in the new array, and use the resulting array that gets returned from .filter:
this.arol = this.arol.filter(x => x.length !== 0);
^^^^^^^^^^^^
If you want to maintain same outer array reference and mutate original you can splice in a loop if you work from end to start so as not to affect indexing you haven't arrived at yet as you change the array length:
const arr = [[1],[],[2]]
arr.reduceRight((_,c,i) => c.length || !!arr.splice(i,1))
console.log(arr)
The problem is you are trying to splice at the same time you are using filter. Filter does not remove elements from your array, it creates a new array with the filtered data, as described here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
You can assign the result to the same array as suggested by CertainPerformance.
If Splice is removed from that code then it will not delete elements. this.arol.splice(this.arol.indexOf(x),1) here it will iterate through every element inside that array and x will be current iteration which will splice from the array as you have written splice method. Remove splice it will work fine.
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.
Why does it say length 1 instead of 4?
The following is what I'm trying to push and slice. I try and append items.image_urls and slice them into 5 each.
items.image_urls is my dictionary array.
var final_push = []
final_push.push(items.image_urls.splice(0,5))
console.log(final_push.length)## gives me 1...?
var index = 0
final_push.forEach(function(results){
index++ ##this gives me one. I would need 1,2,3,4,5,1,2,3,4,5. Somehting along that.
}
items.image_urls looks like this:
It's an iteration of arrays with image urls.
In your example items.image_urls.splice(0,5) returns an array of items removed from items.image_urls. When you call final_push.push(items.image_urls.splice(0,5));, this whole array is pushed as one item to the final_push array, so it now looks like [["url1", "url2", "url3", "url4", "url5"]] (2-dimensional array). You can access this whole array by calling final_push[some_index].
But what you want instead is to add every element of items.image_urls.splice(0,5) to the final_push. You can use a spread operator to achieve this:
final_push.push(...items.image_urls.splice(0,5));
Spread syntax allows an iterable such as an array expression or string
to be expanded in places where zero or more arguments (for function
calls) or elements (for array literals) are expected
This is exactly our case, because push() expects one or more arguments:
arr.push(element1[, ...[, elementN]])
And here is an example:
let items = {
image_urls: ["url1", "url2", "url3", "url4", "url5", "url6", "url7", "url8", "url9", "url10"]
};
let final_push = [];
final_push.push(...items.image_urls.splice(0,5));
console.log(final_push.length);
console.log(JSON.stringify(final_push));
console.log(JSON.stringify(items.image_urls));
Note: do not confuse Array.prototype.slice() with Array.prototype.splice() - the first one returns a shallow copy of a portion of an array into a new array object while the second changes the contents of an array by removing existing elements and/or adding new elements and returns an array containing the deleted elements.
That seems to be a nested array. So if you would access index 0, and then work on that array like below it will probably work:
console.log(final_push[0].length); //should print 4
The author is mixing up splice and slice. Probably a typo :)
You start at the beginning (0) and then delete 5 items.
Can Array.splice() be used to create a sparse array by adding an element at an index beyond the last element of the array?" I need this because in some situations I just want to push onto the array, but in other situations I need to splice into the array. But trying to use splice to make the array sparse did not work, though in my particular situation I was able to implement some code to test whether to use splice, or just assign an array element at an index beyond the array's length.
No. The ECMAScript specification does not allow a relative start position greater than the array length. From ES2015 on Array.prototype.splice, step 7:
...let actualStart be min(relativeStart, len).
The variable actualStart is what's actually used for the splice algorithm. It's produced by the minimum of relativeStart (the first argument to the function) and len (the length of the array). If len is less than relativeStart, then the splice operation will use len instead the actual argument provided.
In practical terms, this means that you can only append values onto the end of arrays. You cannot use splice to position a new element past the length index.
It should be noted the length of the array is not necessarily the index of the last item in the array plus 1. It can be greater.
Then, you can't use splice to insert elements beyond the length of the array, but if you make sure the length is large enough, you can insert beyond the last index plus 1.
var arrSplice = ['what', 'ever'];
arrSplice.length = 10; // Increase the capacity
arrSplice.splice(10, 0, 'foobar'); // Now you can insert items sparsely
console.log(arrSplice.length); // 10
console.log(arrSplice[arrSplice.length - 1]); // foobar
Array.splice() cannot be used to create sparse arrays. Instead, if the index argument passed to Array.splice() is beyond the length of the array, it seems that the element just gets appended to the array as if Array.push() had been used.
/* How you might normally create a sparse array */
var arrNoSplice = ['foo', 'bar'];
arrNoSplice[10] = 'baz';
console.log(arrNoSplice.length); // 11
/* Demonstrates that you cannot use splice to create a sparse array */
var arrSplice = ['what', 'ever'];
arrSplice.splice(10, 0, 'foobar');
console.log(arrSplice.length); // 3
console.log(arrSplice[arrSplice.length - 1]); // foobar