I am working on an algorithm from leetcode.
here is the description:
Given an array and a value, remove all instances of that value in
place and return the new length. Do not allocate extra space for
another array, you must do this in place with constant memory. The
order of elements can be changed. It doesn't matter what you leave
beyond the new length. Example: Given input array nums = [3,2,2,3],
val = 3 Your function should return length = 2, with the first two
elements of nums being 2.
My question is based on the following code:
var removeElement = function(nums, val) {
var count = 0;
for(i=0; i<nums.length; i++){
if(nums[i] == val) {
nums.splice(i,1);
i--;
}
}
};
My question is, why does decrementing with i-- work but incrementing with i++ does not work?
A situation in which my answer is not accepted is when the input array is [3, 3], and the value is 3.
Splice removes the element at i from the array, so you need to stay at the current position, if you want to go on iterating normally. As theres an i++ in the for loop, it would normally go one position forward. So you need to go one step backward before to stay at the same position.
nums=[3,2,2,3]
val=3;
//first iteration
i=0
current=3//===val
nums.splice(0,1);//nums=[2,2,3]
i--;//i=-1
//next iteration
i++;//i=0
current=2;
//next iteration
i++;//i=1
current=2;
//next iteration
i++;//i=2
current=3;
nums.splice(2,1);//nums=[2,2]
i--;//i=1
//next iteration
i++;//i=2 === nums.length => break
In the case [3,2,2,3] it does not matter if you use i++ instead of i-- as it will jump over indexes 1 and 2 wich are not relevant. So this buggy code works by accident...
Related
I have two arrays in Javascript: code and submittedCode. I am trying to compare the two arrays. Each of the arrays is 4 integers long, and each integer is a random value of 1 to 6. I also have two variables: red and white. When the two are compared, the red variable should be set to the number of similarities in the arrays that are the same number, same index. White should be set to the number of similarities in the array that are the same number, but different indexes. For example, if the code array is [1, 3, 6, 5] and submittedCode is [1, 6, 4, 5], then red would be set to 2, and white would be set to 1. This is the same logic as the game Mastermind if anyone has played that. Below is what I have tried, but it is not working as intended.
for(let i = 0; i < code.length; i++) {
if(code[i] == submittedCode[i])
{
code.splice(i, 1);
submittedCode.splice(i, 1);
red++;
//console.log(i);
}
}
console.log(code);
var saveLength = code.length;
code = code.filter(function(val) {
return submittedCode.indexOf(val) == -1;
});
white = saveLength - code.length;
console.log(red + ", " + white);
let arr=[1,3,1,2] //two array we operate on
let arr2=[4,4,1,2]
let red=0
let white=0
//we need to check the current length of remaining array
let currentLength=arr.length
for(let i=0; i<currentLength; i++){
if(arr[i]===arr2[i]){
arr.splice(i,1) //if same number same index, remove this item from array
arr2.splice(i,1)
currentLength-=1 //currentLength-1 because we reduced the array
i-=1 //i-1 because we'd skip the next element
red+=1 //update red
}
}
//we basically do the same thing but with white
//but in the second array we look for the first index of the current element and remove that
for(let i=0; i<arr.length; i++){
if(arr2.includes(arr[i])){
//1: I should've taken away the item from arr2 first
//2: arr2.splice(arr2.findIndex(f=>f===arr[i],1)) notice I messed up where I put the 1 at the end
arr2.splice(arr2.findIndex(f=>f===arr[i]),1)
arr.splice(i,1)
i-=1
white+=1
}
}
Now this might not be the most optimal solution, you can do this in one loop, but for visibility I created 2 for loops, in the first we check the 'red' elements, and we take those out
in the second loop we check if there are any 'white' elements, and we take those out.
I have a single 1d array storing a series of scores. My end goal is to have the 5 highest scores with brackets around them ( e.g. (score) ) for me to then format and output onto the display. In the case where there are duplicate scores, the first occurrences would be bracketed, up to that 5 top values.
So for example:
[9,8,10,9,6,8,6,5,4,4,3,3,6] would become [(9),(8),(10),(9),6,(8),6,5,4,4,8,3,8]
What I've tried so far is this:
var topvals = scores.sort((a,b) => b-a).slice(0,5);
for(var j=0; j< scores.length; j++){
if(topvals.length==0){
break;
}else if(topvals.includes(scores[j])){
scores[j] = "(" + scores[j] + ")";
topvals.splice(topvals.indexOf(scores[j]),1);
}
}
With the idea that topvals is an array containing the top 5 values, and I then loop through scores looking for those values, removing them each time.
What this results in is the first 5 values of scores having brackets around them.
I'm happy to go a completely different route with this, or just fix what I've done so far. Thanks in advance.
sort with indexes attached. Use index positions to change to desired format. O(N log N) for the sort.
scores = [9,8,10,9,6,8,6,5,4,4,3,3,6]
scores.map((n,i)=>({n,i})) // each object as n: number, i: index
.sort((a,b)=>a.n-b.n).slice(-5) // sort, slice top 5
.forEach(({i})=>scores[i]=`(${scores[i]})`) // add parens by indexes
console.log(scores)
If you have very, very large data sets and need something closer to O(N), you'll want to implement a pivot selecting algorithm. Just sorting is simpler.
The call to sort() sorts scores in place, which means it changes the scores array. So that is why you need to clone it first, then sort, then slice. Also you probably want to eliminate duplicates from your scores. I linked a stack overflow answer that describes how to do that. Since filter does not filter scores in place, but rather returns a new array, you don't need to explicitly call slice(0) to clone scores.
var scores = [9,8,10,9,6,8,6,5,4,4,3,3,6];
// Function from linked SO answer.
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
// Filter unique scores into copy of array, then slice the top 5.
var topvals = scores.filter(onlyUnique).sort((a,b) => b-a).slice(0,5);
for (var j=0; j< scores.length; j++) {
if( topvals.length==0) {
break;
} else if(topvals.includes(scores[j])) {
scores[j] = "(" + scores[j] + ")";
topvals.splice(topvals.indexOf(scores[j]),1);
}
}
Get all unique values in a JavaScript array (remove duplicates)
I have an array [1,1,1,0] (any size of), the minimum length of array and number 1 is 3. I want to keep moving the value of 1s 1 element up so it could move to [0,1,1,1], then back to [1,1,1,0].
My loop sort of work in recursive mode, but I'm not sure how to reset the values, so after 4 loops, my entire array is filled with 0's.
More lengthily array would traverse as so:
Example:
[1,1,1,0,0,0]-> [0,1,1,1,0,0]-> [0,0,1,1,1,0]-> [0,0,0,1,1,1]->
[1,1,0,0,0,1]-> [1,1,1,0,0,0]-> [0,1,1,1,0,0]-> [0,0,1,1,1,0]->
And so on for ever...
Code:
function flip(images, active) {
var new_active = active;
setTimeout(function() {
active.reverse();
active[active.length] = 0;
active.reverse();
active.pop();
for(var i = 0; i < active.length; i++) {
alert(active[i]);
}
flip(images, active);
}, 5000);
}
Depending on which direction you want to do, I would suggest using shift() and push(), or pop() and unshift().
Basically, shift() and unshift() add or remove an element from the front of the array, and push() and pop() add or remove an element from the back of the array.
For the example you have given, you'll probably want to pop() off the end of the array and unshift() to the front:
function rotateArray(arr) {
var element = arr.pop();
arr.unshift(element);
}
// Repeat in loop, timeout, etc. as necessary
First pop(store it).So [abcd]->[abc] and you have stored d.
Reverse. So [abc]->[cba].
Add the popped element. So [cba]->[cbad]
Reverse. So [cbad]->[dabc].
I hope you wanted that?
See this too.
Upvote if it helped you. :(
var arr = [1, 1, 1, 0, 0, 0],
startIndex = arr.indexOf(1),
sliceLength = 3,
slice;
while(arr[arr.length - 1] === 0) {
// slice out the 1's (modifies the original array)
slice = arr.splice(startIndex, sliceLength);
// splice the 1's back in, one spot over
arr.splice.apply(arr, [++startIndex, 0].concat(slice));
console.log(arr);
}
http://jsfiddle.net/sn7ujb90/1/
I am trying to remove an item from an Array using Splice method.
arrayFinalChartData =[{"id":"rootDiv","Project":"My Project","parentid":"origin"},{"1":"2","id":"e21c586d-654f-4308-8636-103e19c4d0bb","parentid":"rootDiv"},{"3":"4","id":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02","parentid":"e21c586d-654f-4308-8636-103e19c4d0bb"},{"5":"6","id":"b8d2598a-2384-407a-e2c2-8ae56c3e47a2","parentid":"deca843f-9a72-46d8-aa85-f5c3c1a1cd02"}];
ajax_delete_id = "e21c586d-654f-4308-8636-103e19c4d0bb,deca843f-9a72-46d8-aa85-f5c3c1a1cd02,b8d2598a-2384-407a-e2c2-8ae56c3e47a2";
$.each(arrayFinalChartData, function (idx, obj) {
var myObj = obj.id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
var vararrayFinalChartDataOne = arrayFinalChartData.splice(idx, 1);
}
});
console.log(arrayFinalChartData);
Please check at : http://jsbin.com/deqix/3/edit
Note : It does not complete the "last leg " of the loop. That means if I have 4 items, then it successfully executes 3 items. Same goes for 6,7...items.
I need to "REMOVE" few items and "PRESERVE THE BALANCE" in an array.
You can use for loop instead of $.each function:
alert('length before delete ' + arrayFinalChartData.length);
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
id = arrayFinalChartData[i].id;
if(ajax_delete_id.indexOf(id) > -1){
arrayFinalChartData.splice(i, 1);
}
};
alert('length after delete ' + arrayFinalChartData.length);
Demo.
Complete edit :
After researching a bit, and console.logging a lot, I finally found where the issue is coming from ! It's actually quite simple, but very sneaky !
Theoretical explanation :
You are calling the splice function with the variable "idx", but remember that the splice function remaps / reindexes your array ! So, each time you splice the array, its size decreases by one while you're still inside the $.each function. The splice messes up jQuery indexation of your array, because jQuery doesn't know that you're removing elements from it !
Iterative explanation :
$.each function starts, thinking your array has 4 elements, which is true, but only for a while. First loop, idx = 0, no splice. Second loop, idx = 1, splice, which means that your array has now 3 elements left in it, reindexed from 0 to 2. Third loop, idx = 2, splice, which means your array has now two elements left in it, but $.each continues ! Fourth loop, idx = 3, js crashes, because "arrayFinalChartData[3]" is undefined, since it was moved back each time the array got spliced.
To solve your problem, you need to use a for loop and to start analyzing the array from the end, not from the beginning, hence each time you splice it, your index will decrease as well. And if you want to preserve balance, just push the removed items into an array. Remember that you are analyzing the array from the end, so items pushed into the "removedItems" array will be in reverse order. Just like this :
var removedItems = new Array();
for (var i = arrayFinalChartData.length - 1; i >= 0; i--) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
removedItems.push(arrayFinalChartData.splice(i, 1)[0]);
}
}
console.log(arrayFinalChartData);
console.log(removedItems);
And a working demo (inspect the page, observe the console and click "Run") :
http://jsfiddle.net/3mL6C/3/
I will not give credit to myself for this answer, thanks to another similar thread for giving me a hint.
Your problem here is that when the $.each is set up, it's expecting a certain length of object, which you are then changing. You need to loop in a way that respects the dynamic length of the object.
var i = 0;
while (i < arrayFinalChartData.length) {
var myObj = arrayFinalChartData[i].id;
if (ajax_delete_id.indexOf(myObj) >= 0) {
// current item is in the list, so remove it but KEEP THE SAME INDEX
arrayFinalChartData.splice(i, 1);
} else {
// item NOT in list, so MOVE TO NEXT INDEX
i++
}
}
console.log(arrayFinalChartData);
Demo
I have the following code:
$.each(current, function(index, value) {
thisArray = JSON.parse(value);
if (thisArray.ttl + thisArray.now > now()) {
banned.push(thisArray.foodID);
}
else{
current.splice(index,1);
}
});
There's a problem with the following line:
current.splice(index,1);
What happens is that it (probably) unsets the first case which fits the else condition and then when it has to happen again, the keys don't match anymore and it cannot unset anything else. It works once, but not in the following iterations.
Is there a fix for this?
You can use a regular for loop, and loop backwards:
for(var i=current.length-1; i>= 0; i--) {
var thisArray = JSON.parse(current[i]);
if (thisArray.ttl + thisArray.now > now()) {
banned.push(thisArray.foodID);
} else {
current.splice(i, 1);
}
});
Also it seems thisArray is actually an object, not an array...
you can use regular for loop, but after splice, decrease index by 1
for(var i=0; i<current.length; i++){
current.splice(i, 1); i--;
}
You should pay attention if you mutate an array (by moving objects) while it's being traversed because some element may get skipped like in your case.
When you call current.splice(index, 1) element index will be removed and element index+1 will take its place. But then index will be incremented, thus skipping one element.
A better solution is IMO the read-write approach. You keep one index as the "read pointer" and you always increment it, and another index (the "write pointer") that is incremented only when you decide an element should be kept in the array:
var wp = 0; // The "write pointer"
for (var rp=0; rp<a.length; rp++) {
if (... i want to keep element a[rp] ...) {
a[wp++] = a[rp];
}
}
a.splice(wp); // Remove all elements after wp
This is a o(N) operation and will move each element at most once. Other approaches like starting from the end and using the same splice(i, 1) approach instead will keep moving all elements after i each time an element needs to be removed.