Why does the splice method have this strange behavior? - javascript

In my application I need to delete an element from an array. However I am new to JS. I searched the web and every blog post was talking about splice() method. So I considered to used it, but it has a very strange behavior.
Here the post I found :
http://www.w3schools.com/jsref/jsref_splice.asp
http://viralpatel.net/blogs/javascript-array-remove-element-js-array-delete-element/
Here is my Test:
it("should delete all elements in array", function () {
var ary = new Array();
for (i = 0; i < 10; i++) {
ary[i] = Math.random();
}
expect(ary.length).toBe(10);
for (i = 0; i < 10; i++) {
ary.splice(i, 1);
}
expect(ary.length).toBe(0);
});
And here is the result of the test:
Firefox 15.0.1 Linux: Run 7 tests (Passed: 6; Fails: 1; Errors 0) (44.00 ms)
should delete all elements in array failed (5.00 ms): Error: Expected 5 to be 0.
I use angular JS.
Thanks very much for replies. Here is another test that just don't pass:
var ary = new Array();
ary = ['a', 'b', 'c', 'd'];
ary.splice(0, 1);
ary.splice(1, 1);
ary.splice(2, 1);
ary.splice(3, 1);
expect(ary.length).toBe(0);
Firefox 15.0.1 Linux: Run 7 tests (Passed: 6; Fails: 1; Errors 0) (49.00 ms)
Posting server policy.should delete all elements in array failed (5.00 ms): Error: Expected 2 to be 0.
as #Matteo Tassinari suggest this should one should delete all elements right ??

The reason is simple: with each new splice your array will become shorter:
var arr = [1, 2, 3];
arr.splice(0, 1);
console.log(arr[0]); // 2
console.log(arr[2]); // undefined
console.log(arr.length); // 2
In the second loop of your code splice will change your array only five times. But the sixth time (when i will be equal to 5), arr.splice(5, 1) operation will effectively result in no-op, as your array will be already of 5 elements only.
You can fix it as in #MatteoTassinari answer, or just use splice(0, 1) (or just shift()) instead of splice(i, 1).

For that to work, you'd need to always remove the element at index 0. Otherwise, after, say, 8 elements, you'd be doing ary.splice(8, 1), and given that at this point, the array only has 2 elements left, arr.splice(8, 1) won't remove any, since the index 8 no longer exists.
for (i = 0; i < 10; i++) {
ary.splice(0, 1);
}

As you splice elements from the array, the array becomes shorter. As a result, the last 5 iterations of your loop attempt to splice elements that do not exist.
If you change the code to this:
for (i = 0; i < 10; i++) {
ary.splice(0, 1);
}
It would work as expected by your unit test.

When you splice your array, (and remove one array element), you also "move" all the other array elements forward.
Consider this array before any splicing:
ary = [0,1,2,3,4,5,6,7,8,9]
After ary.splice(0,1), it looks like this:
ary = [1,2,3,4,5,6,7,8,9],
Notice, that the 0th index (ary[0]) is now 1, and when you proceed to do a ary.splice(1, 1), then you don't remove the first element, but actually removes the second element (being 2 in this case)
I know this is not what you're asking for, but a more efficient way to "reset" your array is to do one of these two things:
ary.length = 0;
// or:
ary = [];

Try replacing this:
for (i = 0; i < 10; i++) {
ary.splice(i, 1);
}
with this:
for (i = 0; i < 10; i++) {
ary.splice(0, 1);
}
To delete a specific element, given for example:
ary = ['a', 'b', 'c', 'd'];
if you want to delete the 'c' simply do:
ary.splice(2, 1);
In fact 2 here is the 0-based index of the element which has to be deleted.

Related

Two-sum Leetcode explanation, Hashmap, Javascript

Im just wondering who can explain the algorithm of this solution step by step. I dont know how hashmap works. Can you also give a basic examples using a hashmap for me to understand this algorithm. Thank you!
var twoSum = function(nums, target) {
let hash = {};
for(let i = 0; i < nums.length; i++) {
const n = nums[i];
if(hash[target - n] !== undefined) {
return [hash[target - n], i];
}
hash[n] = i;
}
return [];
}
Your code takes an array of numbers and a target number/sum. It then returns the indexes in the array for two numbers which add up to the target number/sum.
Consider an array of numbers such as [1, 2, 3] and a target of 5. Your task is to find the two numbers in this array which add to 5. One way you can approach this problem is by looping over each number in your array and asking yourself "Is there a number (which I have already seen in my array) which I can add to the current number to get my target sum?".
Well, if we loop over the example array of [1, 2, 3] we first start at index 0 with the number 1. Currently, there are no numbers which we have already seen that we can add with 1 to get our target of 5 as we haven't looped over any numbers yet.
So, so far, we have met the number 1, which was at index 0. This is stored in the hashmap (ie object) as {'1': 0}. Where the key is the number and the value (0) is the index it was seen at. The purpose of the object is to store the numbers we have seen and the indexes they appear at.
Next, the loop continues to index 1, with the current number being 2. We can now ask ourselves the question: Is there a number which I have already seen in my array that I can add to my current number of 2 to get the target sum of 5. The amount needed to add to the current number to get to the target can be obtained by doing target-currentNumber. In this case, we are currently on 2, so we need to add 3 to get to our target sum of 5. Using the hashmap/object, we can check if we have already seen the number 3. To do this, we can try and access the object 3 key by doing obj[target-currentNumber]. Currently, our object only has the key of '1', so when we try and access the 3 key you'll get undefined. This means we haven't seen the number 3 yet, so, as of now, there isn't anything we can add to 2 to get our target sum.
So now our object/hashmap looks like {'1': 0, '2': 1}, as we have seen the number 1 which was at index 0, and we have seen the number 2 which was at index 1.
Finally, we reach the last number in your array which is at index 2. Index 2 of the array holds the number 3. Now again, we ask ourselves the question: Is there a number we have already seen which we can add to 3 (our current number) to get the target sum?. The number we need to add to 3 to get our target number of 5 is 2 (obtained by doing target-currentNumber). We can now check our object to see if we have already seen a number 2 in the array. To do so we can use obj[target-currentNumber] to get the value stored at the key 2, which stores the index of 1. This means that the number 2 does exist in the array, and so we can add it to 3 to reach our target. Since the value was in the object, we can now return our findings. That being the index of where the seen number occurred, and the index of the current number.
In general, the object is used to keep track of all the previously seen numbers in your array and keep a value of the index at which the number was seen at.
Here is an example of running your code. It returns [1, 2], as the numbers at indexes 1 and 2 can be added together to give the target sum of 5:
const twoSum = function(nums, target) {
const hash = {}; // Stores seen numbers: {seenNumber: indexItOccurred}
for (let i = 0; i < nums.length; i++) { // loop through all numbers
const n = nums[i]; // grab the current number `n`.
if (hash[target - n] !== undefined) { // check if the number we need to add to `n` to reach our target has been seen:
return [hash[target - n], i]; // grab the index of the seen number, and the index of the current number
}
hash[n] = i; // update our hash to include the. number we just saw along with its index.
}
return []; // If no numbers add up to equal the `target`, we can return an empty array
}
console.log(twoSum([1, 2, 3], 5)); // [1, 2]
A solution like this might seem over-engineered. You might be wondering why you can't just look at one number in the array, and then look at all the other numbers and see if you come across a number that adds up to equal the target. A solution like that would work perfectly fine, however, it's not very efficient. If you had N numbers in your array, in the worst case (where no two numbers add up to equal your target) you would need to loop through all of these N numbers - that means you would do N iterations. However, for each iteration where you look at a singular number, you would then need to look at each other number using a inner loop. This would mean that for each iteration of your outer loop you would do N iterations of your inner loop. This would result in you doing N*N or N2 work (O(N2) work). Unlike this approach, the solution described in the first half of this answer only needs to do N iterations over the entire array. Using the object, we can find whether or not a number is in the object in constant (O(1)) time, which means that the total work for the above algorithm is only O(N).
For further information about how objects work, you can read about bracket notation and other property accessor methods here.
You may want to check out this method, it worked so well for me and I have written a lot of comments on it to help even a beginner understand better.
let nums = [2, 7, 11, 15];
let target = 9;
function twoSums(arr, t){
let num1;
//create the variable for the first number
let num2;
//create the variable for the second number
let index1;
//create the variable for the index of the first number
let index2;
//create the variable for the index of the second number
for(let i = 0; i < arr.length; i++){
//make a for loop to loop through the array elements
num1 = arr[i];
//assign the array iteration, i, value to the num1 variable
//eg: num1 = arr[0] which is 2
num2 = t - num1;
//get the difference between the target and the number in num1.
//eg: t(9) - num1(2) = 7;
if(arr.includes(num2)){
//check to see if the num2 number, 7, is contained in the array;
index1 = arr.indexOf(num2);
//if yes get the index of the num2 value, 7, from the array,
// eg: the index of 7 in the array is 1;
index2 = arr.indexOf(num1)
//get the index of the num1 value, which is 2, theindex of 2 in the array is 0;
}
}
return(`[${index1}, ${index2}]`);
//return the indexes in block parenthesis. You may choose to create an array and push the values into it, but consider space complexities.
}
console.log(twoSums(nums, target));
//call the function. Remeber we already declared the values at the top already.
//In my opinion, this method is best, it considers both time complexity and space complexityat its lowest value.
//Time complexity: 0(n)
function twoSum(numbers, target) {
for (let i = 0; i < numbers.length; i++) {
for (let j = i + 1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] === target) {
return [numbers.indexOf(numbers[i]), numbers.lastIndexOf(numbers[j])];
}
}
}
}

Is there a possible way to loop through arrays at a specific range rather than loop through the whole thing?

I'm currently going through array exercises to practice my programming on array iteration. I have an array of string objects inside that look like this.
var balling = [
"Calvin Klein", "Tommy Hilfiger", "FUBU", "Rocca Wear",
"Calvin Klein", "Tommy Hilfiger", "FUBU", "Rocca Wear"
];
Now what I know will work is if I come in and loop through all of them like this.
for (var i = 0; i < balling.length; i++) {
balling[i] = console.log(balling[i]);
};
and then the whole thing prints out one by one, top to bottom.
What I would rather do is instead of all 8 of the objects inside that array printing out, I want the for to specify a specific range of objects inside my array.
What exactly do I have to do for my for loop to get the result I want? Is there a way for me to specify how many objects in my array get printed out? Not just one but two, three, and a starting place to start the array and a set range?
You can use Math.min(a,b) function, that gets the lowest value from a or b, so if the array length is smaller than the number of entries you want to get as minimal, you will not have a problem:
var min_entries = 4;
for (var i = 0; i < Math.min(balling.length, min_entries); i++) {
balling[i] = console.log(balling[i]);
};
If you want a differente range, you can also use Array.prototype.slice method:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice?v=example
var a = ['zero', 'one', 'two', 'three'];
var sliced = a.slice(1, 3);
console.log(a); // ['zero', 'one', 'two', 'three']
console.log(sliced); // ['one', 'two']
You can create an array having elements reflecting the range of indexes which should be iterated within a for loop. Increment the first element of range until second element of range is reached.
var balling = ["Calvin Klein", "Tommy Hilfiger", "FUBU", "Rocca Wear"
, "Calvin Klein", "Tommy Hilfiger", "FUBU", "Rocca Wear"];
var [from, to] = [2, 5];
for (; from < to; from++) console.log(balling[from]);
When you declare the 'i' variable in your for loop, that's basically the starting place.
For example, given the array ['a', 'b', 'c', 'd'] and the loop:
for (var i = 1; i < array.length; i++ ) {
console.log(array[i]);
}
that will print out 'b', 'c' and 'd' since the 'i' variable is 1 instead of 0, it won't print the first element. Make sense?
You can change declare a var for start and range that will define how many will show eg:
var start = 1;
var range = 4;
for (var i = start; i < start + range; i++) {
balling[i] = console.log(balling[i]);
};

javascript - potential infinite loop when comparing with arguments.length

I am trying to write a for-loop that goes trough all the inputs of a function, compares the elements of the inputs, and outputs unique elements (in this case they will always be numbers).
Since this is a freecodecamp exercise, I get some feedback, and get the following error: Error: potential infinite loop at line 4.
If I run the loop, without the infinite loop protection I get out of memory. Can someone point out what I'm doing wrong?
function unite(arr1, arr2, arr3) {
//for all arrays
var output = arr1;
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
for(var i = 0; i < output.length; i++) {
if(arguments[x][y] !== output[i]) {
output.push(arguments[x][y]);
}
}
}
}
return output;
}
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
I see two issues with your code.
1: You are adding items to an array as you iterate through it
You set output = arr1', then start walkingarr1as part ofarguments. Then you are adding items intoarr1/output` within the loop. These additional items are traversed and more items added.
2: The unique test is flawed
The first issue may not have been so bad if you ensured the items in the output array were unique. Unfortunately there is also an issue in your test for uniqueness in the output array. The current item is added for each entry in the output array that it doesn't match. If your output were [1,2,3,4] and the item you were comparing was 2, it would get pushed onto the output 3 times because it does not match 1, 3 or 4.

Emptying array when it matches another array

So, I'm trying to match 2 different arrays. If the same cells match, I want to remove that cell from one array using the .slice method.
Edit: What I'm trying to do is remove a number from array1 if array2 contains a matching number. The way the code works now is that it only deletes 1 entry. I want all the entries deleted from the first array.
array1 = [1, 2, 4, 5, 7, 10];
array2 = [1,2,4,5,6,7,8];
var misc = function deleteValues(array, arayy) {
for(var i = 0; i < array.length; i++) {
if ( arayy[i] == array[i]) {
array.splice(i, 1);
}
}
return array;
};
I try to run this and under console log, the array1 is unchanged. It seems like the splice method isn't removing any cells. I searched SE, but couldn't find anything that could help me.
jsFiddle Demo
The problem is that you are modifying one of the arrays as you iterate, but you are still using the same index. The end result is that you end up comparing the wrong indexes to each other after the first removal. Use two indexes, have one offset back down when it removes an item, and have the other simply iterate.
var misc = function deleteValues(array, arayy) {
for(var i = 0, j = 0; i < array.length; i++, j++) {
if ( arayy[j] == array[i]) {
array.splice(i--, 1);
}
}
return array;
};
It seems you want to remove items from the first array if the values are also in the second. The reduceRight method seems suitable as it iterates from right to left over the array, hence removing items doesn't affect the index of subsequent elements in the array. The same result can be achieved with a decrementing loop.
Also, I think function declarations are better than assignment of expressions, but each to their own.
var array1 = [1, 2, 4, 5, 7, 10];
var array2 = [1,2,4,5,6,7,8];
function deleteValues(arr0, arr1) {
arr0.reduceRight(function(x, value, index) {
if (arr1.indexOf(value) != -1) {
arr0.splice(index, 1);
}
},'');
// Not necessary but handy for chaining
return arr0;
}
document.write(deleteValues(array1, array2));
Using an arrow function, the above can be reduced to:
function deleteValues(arr0, arr1) {
arr0.reduceRight((x, v, i) => arr1.indexOf(v) != -1? arr0.splice(i, 1):'');
return arr0;
}

Why is the for loop not removing every odd number from the array (using the splice method)?

Problem
I am trying to remove all of the odd numbers from an array. For example, if I pass an array like so...
var arr = [1,2,3,6,22,98,45,23,22,12]
...the function removes all of the odd numbers except for 23. Why doesn't it remove 23 as well? If different numbers are used or if the order of the numbers is changed, it is always the last odd number that is not removed. I don't understand why though, since the for loop should continue until it gets to the end of the array (i < passedArray.length).
I am sure it is something simple, but I can't figure it out! Any help would be much appreciated ;)
Code
// PROBLEM: Loop through arr removing all values that aren't even.
// The original array
var arr = [1, 2, 3, 6, 22, 98, 45, 23, 22, 12];
// Function to remove all odd numbers from the array that is passed to it.
// Returns the new array.
var getEvenNumbers = function(passedArray) {
for (var i = 0; i < passedArray.length; i++) {
// If the remainder of the current number in the array is equal to one, the number is odd so remove it from the array.
if ((passedArray[i] % 2) === 1) {
arr.splice(i, 1);
}
}
// Return the array with only even numbers left.
return passedArray;
};
// Call the function and store the results.
var evenNumbers = getEvenNumbers(arr);
// Alert the new array that only has even numbers.
alert(evenNumbers);
The bug is that once you've spliced a number out of the array, you still increment i. This makes the code skip the number that follows the one that you've just deleted.
Since the number that precedes the 23 is odd (45), you never look at the 23.
You are chaging your array with splice, so your length changes as well. You could change your function to this:
var getEvenNumbers = function(passedArray) {
var evenArr=[];
for (var i = 0; i < passedArray.length; i++) {
if ((passedArray[i] % 2) != 1) { // if its even, add it
evenArr.push(passedArray[i]);
}
}
// Return the array with only even numbers left.
return evenArr;
};

Categories