My parameter becomes undefined, why is that? - javascript

I am trying to fill my grid with random positions by adding the grid positions to an array and then removing them with a random number as an index. The issue is that it randomly becomes undefined in the middle of my loop. It is the parameter of create_box that becomes undefined.
I'm trying to understand why it becomes undefined, the object gets sent to the function looking as it should. Somehow it doesnt work.
function go(n_columns,n_rows){
let array_of_cells=[];
let number_columns=n_columns+1;
let number_rows=n_rows+1;
for(let columns=1;columns<number_columns;columns++){
for(let rows=1;rows<number_rows;rows++){
let obj={column:columns, row:rows};
array_of_cells.push(obj);
}
}
while(1<array_of_cells.length){
let random_number=Math.floor(Math.random() * array_of_cells.length-1)
array_of_cells.splice(random_number,1);
create_box(array_of_cells[random_number]);
console.log(array_of_cells);
}
function create_box(position){
console.log(position.row)
let box=document.createElement("div");
document.querySelector("#wrapper").appendChild(box);
box.style.backgroundColor="red";
box.style.gridArea = `${position.row} / ${position.column}`;
}
}
go(10, 10);

I think that your issue comes from the fact that you are deleting a value (which reduces the number of indexes in your positions array), before accessing the position at "random_number" index that can sometimes be undefined.
You should probably use the spliced position instead.
while(array_of_cells.length) {
const random_number = Math.floor(Math.random() * (array_of_cells.length - 1)) // note the use of parenthesis here
const position = array_of_cells.splice(random_number, 1)[0];
create_box(position);
console.log(array_of_cells);
}

Related

Trying to increment an integer in an Array

Good Evening,
I am trying to increment an integer that I have index position of '0' in my array, each time my function gets called. I have the variable added with .push, but then I just want to add one to that. I am trying to use indexof(), I have also tried findIndex(). Below is my code
const addFunction = async () => {
var storage_array = await AsyncStorage.getItem(ASYNC_STORAGE_KEY);
try {
if(storage_array) {
storage_array = JSON.parse(storage_array);
let flow_complete = 0;
var foundIndex = storage_array.indexOf(flow_complete);
console.log(foundIndex);
storage_array[foundIndex] = flow_complete++;
await AsyncStorage.setItem(ASYNC_STORAGE_KEY, JSON.stringify(storage_array));
console.log('THIS IS THE ASYNCSTORAGE', storage_array);
} else {
flow_complete = 0;
console.log('Storage array is empty')
}
} catch (error) {
console.log(error);
}
}
Following the re-wording of the issue with your comment:
[...] the objective is to take the number ‘0’ that’s in the 0th position of the array and increment it by 1 each time the function runs
The first issue I see is that you may be misusing the indexOf function. That will not give you the index of an array, but instead the position of a particular value of an array.
Example:
const arr = [9, 2, 7, 14]
const index = arr.indexOf(9) // This will be 0, because the index of the number 9 in this array is 0
const otherIndex = arr.indexOf(7) // This will be 2, because the index of the number 7 in this array is 2
So, for you to access the element in the 0th position, you will want to do arr[0]. So in your code you will want to do the following:
storage_array = JSON.parse(storage_array);
let flow_complete = 0;
// notice there is no need to get the `indexOf` 0 since you do want the position 0
storage_array[0] = flow_complete++;
Now... This will have a second problem which is your usage of the incrementation operator ++. Although this will increment the flow_complete variable, it does not return it to set storage_array[0] as you intend to do so.
To fix this, all you have to do is increment flow_complete prior to assigning it to storage_array[0]. It will look something like this:
let flow_complete = 0;
flow_complete++;
storage_array[0] = flow_complete
However, if my interpretation of your comment above is correct, there is one more problem, which you are assigning flow_complete to storage_array[0] each time the function runs. flow_complete is set to 0 as you can see in your own block of code within the scope of your addFunction, so this means it will always go back to 0 every time it is run.
Going back to your original comment, you want to increment the value in the 0th index of storage_array, not flow_complete itself, correct?
If this is the case, you can completely get rid of the flow_complete variable and instead increment storage_array[0]. This will make your if-block look as follows:
if(storage_array) {
storage_array = JSON.parse(storage_array);
storage_array[0]++;
await AsyncStorage.setItem(ASYNC_STORAGE_KEY, JSON.stringify(storage_array));
console.log('THIS IS THE ASYNCSTORAGE', storage_array);
}

take random string from array, put it in new array, then put it back in old array later

I'm trying to make a sentence that chooses random items from an array twice. The first chosen item is temporarily removed from the array so it can't be chosen again, then the second item is also randomly chosen and added to the sentence. Then, to reset it so I can do this again later, I try to add back the first chosen item in to its initial array.
The issue is that, when I do this, the chosenWord1 displays in the sentence as a string as I hoped, but chosenWord2 displays an index number instead.
array = ["word","another word", "yet another word"];
/* put a random word from the array in its own value and then
remove it from the array so it can't be chosen in next step */
let chosenWord1 = array.splice(Math.random() * array.length, 1)[0];
/* pick another random word from the same, altered array */
let chosenWord2 = Math.floor(Math.random() * array.length);
/* a function that puts a sentence on the webpage concatenating the strings
from chosenWord1 and chosenWord2 goes here. then... */
/* add back the previously removed word so this process can repeat again
with the same items in the array as there was before anything was done to it */
array.splice(1,0,chosenWord1);
Also, if there's some more sensible way I can structure this, please let me know. I'm still pretty new to this.
I don't know if this is what you need, but this extracts two random words repeatedly without taking them out from the original array. The for loop is used to show how it works with different words.
const array = ["word", "another word", "yet another word"];
const randomNumber = (key) => Math.floor(Math.random() * key);
for (let i = 0; i < 10; i++) {
const { [randomNumber(array.length)]: wordOne, ...rest } = array;
const { [randomNumber(array.length - 1)]: wordTwo } = Object.values(rest);
console.log(`${wordOne} and ${wordTwo}`);
}

How to update a variable that is returned from a function?

I've been trying to write a go back function. What this function will do is it'll store last two ID number that has been generated and append when there is a new one, delete the first one.
I have this function which creates the IDs and plays the video with that ID.
function newVideo(){
let rando = Math.floor((Math.random() * 3970) + 1);
document.getElementById("vsrc").src = "https://raw.githubusercontent.com/ondersumer07/vinematik-videos/master/vid/" + rando + ".mp4";
document.getElementById("videoEl").load();
return rando;
}
I am returning the rando to use it outside this function, and I can access it outside the function, the problem is, the variable outside the function is not updating everytime newVideo() run.
The code for that goes like this:
let rando = newVideo();
let vidids = [];
if (vidids.length < 2) {
vidids.push(rando)
console.log("added")
} else {
vidids.shift()
console.log("deleted")
};
Basically what this does is to get the returned rando value and push it to the vidids array, delete the first one if there is more than two but it won't, it does not update the let vidids = []; array for some reason. I need it to update everytime the newVideo() function ran.
Also, I want the if to add if there is less then two items in that array and delete from the start of the array if there is more than two items in it. Not really sure if that'll work too.
I can't seem to figure out how to do this, am I doing this whole thing wrong or is there still hope for this function? How am I supposed to write that function?
Edit: I've changed the vidids.length part yet the problem still occur because let rando = newVideo(); doesn't update.
In order to keep the last two Items of an array, you can use something like this.
let vidids = [];
function addToArray(item, array){
array.unshift(item);
return array.slice(0,1);
}
//test
vidids = addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
console.log('Done');
You've made a small mistake that caused your code to not work properly.
The IF condition is intended to check the length of the 'vidids' array,
What actually happens is that it compares the array referance instead of it's length
To fix the issue, add .length after 'vidids' inside of the IF condition.
....
if (vidids.length < 2) {
.....
I figured it out. Basically what I did is storing every number the algorithm creates and then taking one before the last.
Which goes like this:
The algorithm for creating random numbers:
function randomNum() {
let rando = Math.floor((Math.random() * 3970) + 1);
return rando;
};
Then I put this function in a variable:
let videoid = randomNum()
I had another variable called videoids which is above and outside of the randomNum function and is an array:
let videoids = []
After that I stored every number I created with videoid inside videoids by pushing it(This push needs to be inside your function):
videoids.push(videoid);
Okay so I stored all the numbers this way. Now I should take one before the last so I can go to previous video. So I needed to create a function, I used this function which was created by Tadeck, in this thread.
if (!Array.prototype.last) {
Array.prototype.last = function() {
return this[this.length - 2];
};
};
Now I can put that inside of my prevVideo function which looks like this:
function prevVideo() {
document.getElementById("vsrc").src = srcRaw + videoids.last() + ".mp4";
document.getElementById("videoEl").load();
videoids.push(videoids.last());
}
Note: Don't forget to push videoids.last inside your videoids otherwise you can only go to your previous number for once.

API response is returning undefined

I am making a API call that returns a JSON response of an Array with a bunch of objects. Each object has a key "dt" that is the timestamp of a specific time of day and another key of "height" which is the Ocean's predicted or past tide height at that moment in time.
I only want the current tide's height at whatever moment in time the AJAX call happens. This is the function I created in order to achieve that:
let tideApi = 'https://www.worldtides.info/api?heights&lat=45.202&lon=-123.963&key=24e4ff14-6edf-4f79-9591-e47f9e0e21e1';
$.getJSON(tideApi, function(response) {
// Create Global variable
let tideHeight = 0;
// get current time
let nowMil = new Date().getTime();
// round that time to the nearest halfhour and convert back to timestamp (JSON timestamps are set in intervals of 30 minutes)
let timeNow = Math.round(nowMil/1000/60/30) * 30 * 60 * 1000;
// set entire array to variable
let heightArray = response.heights;
// get length
len = heightArray.length
// loop through each object in height array
for (var i=0; i < len; i++) {
// if one of the objects timestamp equals current time
if (i.dt = timeNow) {
// set tide height to variable
tideHeight = i.height;
// return tide height (curretly returning undefined)
console.log("Tide Height: " + tideHeight);
return tideHeight;
} else {
console.log("Error, no time found");
}
}
// put tide height into div
$("#tideStat").append("<p>" + tideHeight + "</p>");
});
It's currently returning undefined for a reason I am struggling to figure out. Any help would be great!
API Call (Don't worry going to change after this)
Codepen
There are a few problems in your code
let timeNow = Math.round(nowMil/1000/60/30) * 30 * 60 * 1000;. Your API doesn't seem to be returning milliseconds. Remove the * 1000.
You're not accessing items from your heightArray. Rather, just checking dt and height property on i, which is an integer. So change i.dt and i.height to heightArray[i].dt and heightArray[i].height respectively.
When you use if (lhs = rhs), you're attempting to assign, not compare. So, change = to === in the if condition.
Remove return tideHeight;. I think you want break? Not sure though. Because of this line, your last jQuery related code doesn't execute.
Forked pen. Some logs commented out for better output.
Use bracket notation to reference i object of heightArray array, === operator, instead of =, which is assignment operator
if (heightArray[i].dt === timeNow) {
// do stuff
tideHeight = heightArray[i].height;
}

I'm having trouble writing a recursive function in JavaScript - it doesn't seem to "fall back" to the lesser depth correctly

var diceToRoll = [2,2];
var diceRolled = new Array();
function recurse(diceToRoll, diceRolled) {
roll = diceToRoll[0]
diceLeftToRoll = diceToRoll;
diceLeftToRoll.shift();
for(loop=1; loop<(roll+1); loop++) {
result = diceRolled;
result.push(loop);
if(diceLeftToRoll.length == 0) {
console.log(result);
result.pop();
} else {
recurse(diceLeftToRoll, result);
}
}
}
recurse(diceToRoll, diceRolled);
I'm trying to write a recursive function which prints the possible results of any number of dice. For example, a dd100 (diceToRoll = [6, 10, 10, 100])(diceToRoll = [6, 6, 6, 6, 6]) etc. In the example I have used the simplest case(or two 2-sided dice).
I expected the results to be [1,1], [1,2], [2,1], [2,2] however it only logs [1,1], [1,2]. This is the same with any number or type of dice - only the deepest level of recursion works correctly.
I figure I'm missing something obvious in the logic of it / or misunderstanding variable scope in JavaScript, but I'm just really struggling to get my head around it.
Edit 1 (To make explanation of program's purpose clearer)
The programs purpose is to list all possible values on any number of dice. So a dice 6 implies the range of values 1..6. Likewise, a two-sided dice, 2, implies the range of values 1..2. So for two two-sided dice in the example (diceToRoll[2,2]) the possible values are 1,1 1,2 2,1 and 2,2 - which is what should be returned.
There are several issues with your code:
Use var keyword in order to define local variables.
Assigning array to another variable not copies its content, just reference to the same array. Use Array.slice() if you want to clone array.
Here is a fixed function:
var diceToRoll = [2,2],
diceRolled = [];
function recurse(diceToRoll, diceRolled) {
var roll = diceToRoll[0],
diceLeftToRoll = diceToRoll.slice(1),
loop,
result;
for(loop=1; loop<=roll; loop++) {
result = diceRolled.slice(0);
result.push(loop);
if(diceLeftToRoll.length === 0) {
console.log(result);
result.pop();
} else {
recurse(diceLeftToRoll, result);
}
}
}
recurse(diceToRoll, diceRolled);
NOTE
diceToRoll = diceToRoll.slice(1)
is equivalent to
diceToRoll = diceToRoll.slice(0);
diceToRoll.shift();
Fiddle here: http://jsbin.com/isebef/1/edit
You must declare "roll" (and other local variables) with var.
function recurse(diceToRoll, diceRolled) {
var roll = diceToRoll[0]
var diceLeftToRoll = diceToRoll;
diceLeftToRoll.shift();
for(var loop=1; loop<(roll+1); loop++) {
var result = diceRolled;
result.push(loop);
if(diceLeftToRoll.length == 0) {
console.log(result);
result.pop();
} else {
recurse(diceLeftToRoll, result);
}
}
}
Without var, "roll" and "loop" are global.
I'm not sure what the point of "result" is; it's just a reference to the "diceRolled" array, so I'm not sure why you wouldn't just use that.
edit — I'm not sure exactly what your code is trying to do here, but another significant problem is this:
var diceLeftToRoll = diceToRoll;
diceLeftToRoll.shift();
When you make an assignment like that of a value that's a reference to an array, you do not make a copy of the array. Thus both variables refer to the same array object, and the first element will be removed from it. If you instead make "diceLeftToRoll" a copy of the other array, then things work differently:
var diceLeftToRoll = diceToRoll.slice(1); // copy all but 1st element
I don't think the whole thing will work, however, because I now think that the "result" variable was an attempt to do something similar.
edit again Here is an alternative version that returns results in a list. This one avoids making copies except for the final entries added to the result.
function allRolls( dice ) {
var list = [], rolled = [];
function roll( dn ) {
var dp = rolled.length;
for (var dv = 1; dv <= dice[dn]; ++dv) {
rolled[dp] = dv;
if (dn < dice.length - 1)
roll(dn + 1)
else
list.push(rolled.slice(0));
}
rolled.length = dp;
}
if (dice.length) roll(0);
return list;
}
allRolls([3, 3, 3]);
The function involves an inner function that does all the work. It's passed the index in the "dice" list of a dice to roll; initially, that's 0.
The function keeps track of two other lists: the accumulated possible rolls, and an array representing the "rolled so far" for use by the recursive inner function.
At each recursive level, the function iterates through the faces of the current dice (that is, dice[dn]). Each iteration places that dice value in a slot at the end of the "rolled" array; the same slot is used for each iteration. Now, if the loop notices that "dn" represents the last dice in the list, then it makes a copy of the "rolled" array and adds it to the result list. If not, then it makes a recursive call, passing the next dice index down.
The outer function then just checks to see if there are any dice to roll, and rolls them if so. It returns the accumulated list.

Categories

Resources