My goal: To clean up localstorage, by removing previously used items with a for loop.
Usually, I have a number of items, like so:
order-1356666288243 => {"cartID":2713329701836,"productName"...
order-1356666288243 => {"cartID":2713329701836,"productName"...
When I check how many items there are altogether, I get the correct zero-based amount:
console.log( localStorage.length );
Even when I do a for loop to write out the key and values, and console.log() a few other things, all is well:
for(var i=0, len=localStorage.length; i<=len; i++) {
var key = localStorage.key(i);
var value = localStorage[key];
console.log(key + " => " + value);
if(key != '' && key != null){
console.log( key.indexOf('order-') );
console.log(i + '. Key: ', key);
if(key.indexOf('order-') != -1){
console.log('The key to be removed is: ', key.toString());
localStorage.removeItem(key.toString());
}
}
console.log(i + ': ' + len);
}
Everything pretty much does what one would expect. However, this line executes only once when I run the script:
localStorage.removeItem(key);
or
localStorage.removeItem(key.toString());
In fact, I have to run the entire script as many times as there are items. So if I have, say 3 items, I need to run the loop 3 times to get rid of each item.
I'm perplexed: Where did I go wrong? All the console.log() stuff shows up 3 times (if I have 3 items and run the for loop once) . Out of despair, I even changed i < len to i >= len, but that doesn't solve the problem either.
Anyone?
The problem is that you are modifying a collection while you are traversing it. Always be careful when you do this.
In this particular case, the length of localStorage changes when you remove an item, so you end up skipping items.
You should either traverse localStorage backwards (last item to first item), or better yet you should do this in two passes: one pass to collect the keys to remove, and another pass to remove them:
Object.keys(localStorage)
.filter(function(key){return key.indexOf('order-')===0})
.forEach(localStorage.removeItem, localStorage);
Or if you need this to work in IE8 or FF3.x:
var delkeys = [], key, i, len;
for (i=0,len=localStorage.length; i<len, key=localStorage.key(i); i++) {
if (key.indexOf('order-')===0) {
delkeys.push(key);
}
}
for (i=0,len=delkeys.length; i<len, key=delkeys[i], i++) {
localStorage.removeItem(key);
}
I think the problem is that when you remove an item, it changes the length of the local storage. Try iterating from the length-1 down to 0 instead of from 0 up to the length. (By the way, the loop condition should have been i < len, not i <= len. With my suggestion, of course, it should be i >= 0.)
The loop should count down to prevent problems with deleted keys altering storage length. Try this for loop instead:
for( var i = localStorage.length; i--;){
//make sure to remove references to len in your code
}
Related
I'm doing a Dynamic Programming excercise about making combination for array n size with result of k numbers combination and stumble upon this solution, I'm trying to understand what end-i+1 >= r-index is doing here can someone explain.
is this to make the current index make combinations with the other remaining index, still how does that work
function combinationUtil(arr,data,start,end,index,r)
{
// Current combination is ready to be printed, print it
if (index == r)
{
for (let j=0; j<r; j++)
{
document.write(data[j]+" ");
}
document.write("<br>")
}
// replace index with all possible elements. The condition
// "end-i+1 >= r-index" makes sure that including one element
// at index will make a combination with remaining elements
// at remaining positions
for (let i=start; i<=end && end-i+1 >= r-index; i++)
{
data[index] = arr[i];
combinationUtil(arr, data, i+1, end, index+1, r);
}
}
// The main function that prints all combinations of size r
// in arr[] of size n. This function mainly uses combinationUtil()
function printCombination(arr,n,r)
{
// A temporary array to store all combination one by one
let data = new Array(r);
// Print all combination using temporary array 'data[]'
combinationUtil(arr, data, 0, n-1, 0, r);
}
for(var i=0; i < imageFiles.length; i++)
{
console.log('index value : ' + i)
let item = imageFiles[i]
let file = item.getAsFile()
let oValue = await self.getOrientation(file)
console.log('orientation value : ' + oValue)
}
The for loop above is only executing once, could someone provide a solution that will make the loop iterate over all the files in the array ?
From within the parentheses, you defined the variable "i" with a value of 0. As a result, i++ will return 1 and also save it to "i". You said it's only executing once for some reason, clearly this is part of the issue.
Check to make sure any code related to this is written as intended.
I am working on a JS where I want to create a simple game that starts by chosing number of players, name of each player and whether a player is a dealer or not. There can be only one dealer for each game:
function player(playerName, playerDealer) {
this.playerName = playerName;
this.playerDealer = playerDealer;
}
var playerNumber = prompt('Nr of players?');
var playersArray = [];
for (i = 0; i < playerNumber; i++) {
var j = i + 1;
var dealerAssigned = false; // control variable to check whether dealer has been assigned
var inputName = prompt('Name of player nr ' + j);
var inputDealer = prompt('Is player ' + inputName + ' also a dealer? (yes/no)');
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++){ // I want to go through the players array to check if dealer has been assigned
if (playersArray[k].playerDealer == 'yes') {
dealerAssigned=true;
break;
};
};
if(dealerAssigned){ //if dealer has been assigned, don't add the current player to the array and continue with the next iteration
alert("already assigned");
continue;
};
};
I need to include a simple test into the loop that would check if the dealer has been appointed. If so, I want the script only to alert 'already assigned' and skip to the next player. But I am constantly getting the following error
TypeError: playersArray[k] is undefined
Can anybody explain why is it undefined?/What am I doing wrong?
The bug you're specifically asking about appears to me to be that you're iterating over undefined array values, as the error you're getting suggests.
You're getting the number of players you want in line
var playerNumber = prompt('Nr of players?');
Then, you proceed to have two iterations (one nested in the other), in which the inner loop is trying to access values that haven't yet been assigned since the outer loop hasn't gotten there yet:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0; k < playerNumber; k++) {
if (playersArray[k].playerDealer == 'yes') {
...
}
}
}
It appears to me that the logical error here is the nested loop. I recommend just initializing all players in one loop, then verify that all players have an assigned dealer afterward.
I should add that I'm being intentionally myopic here and focusing very narrowly on the question asked and overlooking other issues I see.
Your for loop inside a for loop is iterating over an array that hasn't been filled yet.
First iteration playersArray[j] = new Player(...) makes the array [Player] or an array of one element! Yet the second loop is looking for an array of many elements. once you look for playersArray[1] but there is only playerArray[0] you get undefined and so undefined.playerDealer causes a TypeError.
`This is your structure stipped-down:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++)...{
//anything with index k > i is undefined, since your outer loop
//hasn't initialized it yet.
}
}
It seems that your i-loop is trying to insert elements for the size of the array to be, but your k-loop is trying to also access the entire array instead of just the initialized portions. Limit this to for (k=0; k<i+1 ;k++) so you only check the previously initialized values of you playersArray
I have a form where the user can add X number of rows, with each row having a start time and end time input.
The rows can be added as the user likes, and the times do not have to be entered sequentially, but must not conflict when submitting the form.
So far, I am able to check for conflicts, using a couple of for loops and checking each start and end time against the rest.
The problem I am facing, is that obviously, if row 1 and 2 conflict, my code is logging two conflicts (logically correct!)
I only want to show the first conflict, as once this is resolved, naturally, the second conflict is.
My code for far:
$('form').submit(function(event){
event.preventDefault();
var errors = [];
var data = serializedToObject($(this).serializeArray());
for(var i = data.row.length; i--;) {
for(var s = data.start.length; s--;) {
if(s != i) {
if(data.start[i] < data.end[s] && data.start[s] < data.end[i]) {
errors.push('Conflict between ' + data.row[i] + ' and ' + data.row[s]);
}
}
}
}
if(errors.length === 0) {
this.submit();
} else {
console.log(errors);
}
});
(serializedToObject simply converts the form data to an object)
So how do I have my code only push 1 of the conflicts to the array?
I tried adding the row ID to an object and pushing that to the array, but it wouldn't log additional conflicts later down the line e.g. Row 1 conflicts with 2 and 4, the conflicts between 1 and 4 would not be mentioned, as row 1 is already in the array.
I have an answer for you, but it is not that efficient (again O(n^2), as you code in the question ).
If i understood correctly , data.start.length and data.row.length has to be equal, right? If so, if you count the s from i-1 to 0, and the errors are (1,2) and (2,1) , (1,2) won't get cought becouse the second loop starts from i-1, in the case where i=1, s starts directly from 0. just take a look at the code below (only included the for loops) :
var length = data.row.length;
for( var i = length; i>0; i-- ) {
for( var s = i-1; s>0; s-- ) {
if(data.start[i] < data.end[s] && data.start[s] < data.end[i]) {
errors.push('Conflict between ' + data.row[i] + ' and ' + data.row[s]);
}
}
}
I hope somebody will come with a comment with an idea to optimise this, maybe O(n) if it is possible :D. But this will work for you , and in case where you don't have length variable as big as 100.000, it will work just swell!
for(var i = 0; i < this.phrases.length; i++) {
console.log('sort ' + this.phrases[i]);
console.log('sort length' + this.phrases.length);
if(this.phrases[i] == null) {
var s = this.phrases.splice(i, 1);
console.log('splice ' + this.phrases[i]);
console.log('splice length ' + this.phrases.length);
}
}
I have an array (this.phrases). I made the phrase that I want to remove equal to null in another section. The first log prints null, the second log also prints null. Why is it not getting spliced? This is also sometimes the last item in an array. Are you not able to splice an array with only one element? The same thing happens, however if it is not the last item in this.phrases.
Edit: s also does not seem to get set to any value.
Edit: The two length logs I added print the same number.
Edit: That's not actually true. Weird things are happening with the lengths, probably not involving this section of code. I just want to know if I'm using splice() correctly.
Switch these two lines:
var s = this.phrases.splice(i,1);
console.log('splice ' + this.phrases[i]);
To this:
console.log('splice ' + this.phrases[i]);
this.phrases.splice(i, 1);
i--;
const indexOfValue = treeSelectDataTemp.findIndex((item: any) => item.value === deleteIndexId[d]);
console.log(" ########### deleted indexOfValue ", indexOfValue);
if (indexOfValue !== -1) {
treeSelectDataTemp.splice(indexOfValue, 1);
}