Array of objects properties become NaN between function calls in Html Javascript - javascript

This code is done in HTML javascript.
The following functions (placero() and runro()) are called in that order. placero() seems to initialize the xs and ys of the object list correctly, but by the time the first line of runro() is called, all of the xs and ys have become NaN.
The goal of the code was to make a program that creates rooms and connects them with corridors. Previous steps have inititalized the rooms and corridor layout, but the job of placero() is to place each room somewhere random on the map.
the code is as follows:
runm does all the initial setting code(such as the room member initialization) and does all the other work before placero(). I have been clicking the buttons in the intended order(sequentially), so that shouldnt be the problem.
var roomes = new Array(0);
function randIntBetween(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
//before any function calls, rooms is initialized as follows(well after the input has been entered, in most cases):
roomMax = document.getElementById("inp1").value; //this is the inputted number of rooms being made
for (var k = 0; k < roomMax; k++) {
var obj = {
ind: k,
x: -1,
y: -1,
neighb: Array(0)
}
roomes.push(obj);
}
//then some work is done, placing the indexes of other rooms in the neighbors array in each room.
//none of the in-between code edits x or y.
function placero() {
for (var kis = 0; kis < roomes.length; kis++) {
var x = randIntBetween(5, mapX - 5); //get a random x and y for room position
var y = randIntBetween(5, 5);
roomes[kis].x = x;
roomes[kis].y = y;
}
console.log(roomes); //prints a correct array
}
function runro() {
console.log(roomes); //prints an incorrect array
var iterCount = 2;
//this code aims to place rooms closer to their neighbors, by averaging a rooms position with that of its neighbors and moving it half the way in that direction.
for (var ki = 0; ki < iterCount; ki++) { //for each iteration in which the rooms are moved,
for (var kt = 0; kt < roomes.length; kt++) { //for each room
var coun = NeighbCount(roomes[kt]); //get number of neighbors(will be used for averageing)
console.log(coun);
var sumx = 0;
var sumy = 0;
for (var km = 0; km < coun; km++) { //for each neighbor,
sumx = sumx + roomes[roomes[kt].neighb[km]].x; //add its position to the sum position
sumy = sumy + roomes[roomes[kt].neighb[km]].y;
}
sumx = sumx / coun; //then divide by number of neighbors to get new position
sumy = sumy / coun;
console.log(sumx + " " + roomes[kt].x); //troubleshooting
console.log(sumy + " " + roomes[kt].y);
roomes[kt].x = sumx / 2 + roomes[kt].x / 2; //setting new positions
roomes[kt].y = sumy / 2 + roomes[kt].y / 2;
}
}
}
<div>
<input type="number" id="inp1" name="inp1"><br>
</div>
<button onclick="runm()">Make room map</button>
<button onclick="placero()">place rooms</button>
<button onclick="runro()">Make rooms</button>

You're probably getting the error because roomMax is of string type and not number type. To solve this, make the following modifications to your code:
roomMax = parseInt(document.getElementById("inp1").value);
The value from an input will always be a string. The code above changes the type.
Look through your code to see if you have any similar mistakes.

Related

I want to write a Google App Script to automate calculating late days in Google Sheet

I want to write a code that automate the late days calculation. Here is how it should work
If the score of date after (ex: date 10) - score of day before (date 9)> 0 => the score of date after (120) will be assigned "late for 3 days"
On the other hand, that date after will keep looking and substracting from the next date before until it gets "> 0" result.
The example is illustrated in attached image.
Here is the code that I currently have and it didnt work:
function myFunction() {
var app = SpreadsheetApp;
var activeSheet = app.geActiveSpreadsheet().getActiveSheet();
for (var i=2; i <= 8; i++){
var scoreafterCell = activeSheet.getRange(i;2).getValue();
var scorebeforeCell= activeSheet.getRange(i++;2).getValue();
scoreDiff= scoreafterCell- scorebeforeCell;
if(scoreDiff > 0) {
activeSheet.getRange(i,3).setValue(3);
} else for (var k=0; k++) {
do {
lateDays= k+=3;
activeSheet.getRange(i,3).setValue(lateDays);
}
while (scoreDiff = 0);
}
}
}
Hope to have some inputs soon since this is very important for my work!!! Thank you.
Since there is no clear pattern above, I did a hack to achieve what you want. This only checks the scores and doesn't check the dates as lateDays won't be dependent on that.
Please see the code below.
function generateLateDays() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastRow = activeSheet.getLastRow();
var scores = activeSheet.getRange('B2:B' + lastRow).getValues().flat();
var output = activeSheet.getRange('C2:C' + lastRow);
var lateDays = [];
while(scores.length > 0) {
// get how many occurences does the first element have
occurence = scores.filter(value => value === scores[0]).length;
// remove current item in advance, we will only be checking number of occurences
// exact value will not be checked, so scores should be in descending order
scores = scores.filter(score => score !== scores[0]);
// contains per score late days
var temp = [];
for(var j = 0; j < occurence; j++) {
if(scores.length > 0) {
// e.g. 3 duplicates will make temp 3, 4, 5
temp.push(j + 3);
}
else {
// when last element is up, push 0
temp.push(0);
}
}
// e.g. temp reverse will be 5, 4, 3
temp.reverse();
// add to final output
lateDays.push(temp);
}
// convert 2d array to 1d array
lateDays = lateDays.flat();
for(var i = 0; i < lateDays.length; i++) {
// convert all elements into an array for setValues
lateDays[i] = [].concat(lateDays[i]);
}
output.setValues(lateDays);
}
Note that this will work only on the order your sample sheet works. I haven't tried doing it ascending. Since your sheet score descending, I made this to work that way.
If you have any questions, feel free to ask below.
Below is my suggested answer. Check it out:
function getLateDates() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var scores = activeSheet.getRange('B2:B10').getValues().flat();
var output = activeSheet.getRange('D2:D10');
/**
* Set LateDates[_] as array of 3,
* except the last value = 0 ( at t = 0 )
*
*/
let LateDates = [
...Array.from({ length: scores.length - 1 }).map((x) => 3),
0,
];
/** Check the diff of current score with each of the next score,
* if diff > 0 --> break --> LateDates[current] = 3
* if diff = 0 --> +1 to LateDates[current], and continue w the next score
*
*/
for (var current = 0; current < scores.length; current++) {
for (var next = current + 1; next < scores.length; next++) {
let d = scores[current] - scores[next];
if (d > 0) {
break;
} else {
LateDates[current]++;
}
}
}
/**
* Reduce the LateDates to fit setValues format
*/
let result = LateDates.reduce((a,c)=> ([...a,[c]]),[])
output.setValues(result)
//console.log('result', result);
}
Notice I'm using your date order (descending).
If it's ascending, switch 0 to top of LateDates[] instead:
let LateDates = [
0,
...Array.from({ length: scores.length - 1 }).map((x) => 3),
];

How Do I Use Loops to Find Sums in Array?

So I need to use a loop to fill an array with the total amount. The total amount will be the amount spent plus a gift card that is a percentage. So
total = amountSpent + amountSpent*(1+giftCard)
I am having trouble getting the total. The spent and gift card amounts are all randomly generated using math.random. The spent and gift card amounts are each found in separate arrays, with the spent amount being anywhere between 0 to 500, and gift card amount being anywhere from 0 to 50.
var spent = new Array(5);
for (var i = 0; i < 5; i++)
{
randS = Math.floor(Math.random() * 500);
spent[i] = randS;
}
var gifts = new Array(5);
for (var i = 0; i < 5; i++)
{
randG= Math.floor(Math.random() * 50);
gifts[i] = randG;
}
These are how I fill the arrays using a for loop. I am now supposed to create a new array and use a loop to calculate the total. I defined 2 variables for spent and gift card amount, but I am unsure if they are calling the correct numbers.
var totals = new Array(5);
var tSpent = spent;
var tGifts = gifts;
for (var i = 0; i < 5; i++)
{
totals[i] = tSpent + (1 + (tGifts / 100)) * tSpent;
totals[i] = totals[i].toFixed(2);
}
I know this array is the problem since the other two arrays are displaying the numbers fine. I also have to convert the gift card amount to a decimal and make sure the total is to 2 decimal places.
you can write the whole code in one loop like this:
var spent=[],gifts=[],totals=[];
for(let i = 0; i < 5; i++){
spent[i] = Math.floor(Math.random() * 500);
gifts[i] = Math.floor(Math.random() * 50);
totals[i] = spent[i] + (1 + gifts[i]/10)*spent[i]
totals[i] = totals[i].toFixed(2);
}
As far as I am able to understand your question, I feel you are doing it correct, just a small correction needs to be done in your code.
var totals = new Array(5);
var tSpent = spent;
var tGifts = gifts;
for (var i = 0; i < 5; i++)
{
totals[i] = tSpent[i] + (1 + (tGifts[i] / 100)) * tSpent[i];
totals[i] = totals[i].toFixed(2);
}
Also, your arrays tSpend and tGifts are calling correct numbers, although they seem redundant, unless you have something planned for them later in code. If not, they are just directly referencing your actual variable spend and gifts.

Javascript, drawing text from a sprite sheet, simulating a carriage return

I am making a game in javascript/html5. I am generating random thought balloons composed entirely of sprites. The balloon itself and the text. I get all of this working no problem, however, I am trying to simulate a carriage return so that the "strings" are not too long. This is the part which is not working. I am frustratingly close to a solution I can FEEL IT. After the last 72 hours of tackling this problem though I am too close to it, I think and need some outside eyes.
To accomplish this, I devised a system whereby I have the essentially a 2-dimensional array:
var testThoughts = [
["fragment 1 ", "fragment 2 ", "fragment 3"],
["fragment 1 ", "fragment 2 ", "fragment 3"],
["fragment 1 ", "fragment 2 ", "fragment 3"],
["fragment 1 ", "fragment 2 ", "fragment 3"]
];
Something like that. I pick a random thought and then pass it to a function that maps the letters in the string to corresponding images on the sprite sheet. After that, I render the sprites to the screen. The way the code is set up is each fragment converted to sprites then pushed then rendered in turn. This is where I'm stuck. No matter what I do I cannot get the three fragments to stack one on top of the other like this:
//This is a
// really
//long sentence.
They are either offset, or all the fragments stack properly but
the sprites in each fragment are rendered on top of one another.
Here is what the render code looks like:
function render(array) {
//console.log(array);
context.clearRect(0, 0, canvas.width, canvas.height);
var array = array;
for(var l = 0; l < array.length; l++) {
var letterSprite = array[l];
letterSprite.x = l * kearning;
letterSprite.y = leading; //Trying to offset the y, I have tried += and = l*leading
context.drawImage(
art,
letterSprite.sheetX,
letterSprite.sheetY,
letterSprite.width,
letterSprite.height,
letterSprite.x,
letterSprite.y,
letterSprite.width,
letterSprite.height
)
}
}
I have attempted many permutations to solve this. Messing with both x and y as well as nested for loops etc. I even tried creating a newLine object so that I could manipulate its y value instead to no effect. The behavior is identical.
Any assistance is greatly appreciated.
UPDATE
I'm posting the entirety of the logic, as I think it will prove useful in discovering a solution:
function loadHandler()
{
assetsLoaded++;
if(assetsLoaded === assets.length)
{
art.removeEventListener("load",loadHandler,false);
displayThought(testThoughts)
}
};
function displayThought(array)
{
var array = array;
var randomIndex = getRandom(array);
for(var f=0; f<array[randomIndex].length; f++)
{
testString = array[randomIndex][f];
convertStringToImages(testString);
}
};
function getRandom(array)
{
var array = array;
var randomIndex = Math.floor(Math.random()*array.length);
return randomIndex;
};
function convertStringToImages(string)
{
var string = string;
var splitString = string.split("");
for(var s=0; s<splitString.length; s++)
{
switch(splitString[s])
{
case "A":
var letterSprite = new LetterSprite(1,1,8,10);
output.push(letterSprite);
break; //redacted for brevity...
}
}
render(output);
};
function render(array)
{
counter++;
//console.log(array + " number " + counter);
context.clearRect(0,0,canvas.width,canvas.height);
var array = array;
for(var l=0; l<array.length; l++)
{
var letterSprite = array[l];
letterSprite.x = l * kearning;
letterSprite.y += leading;
context.drawImage
(
art,
letterSprite.sheetX,
letterSprite.sheetY,
letterSprite.width,
letterSprite.height,
letterSprite.x,
letterSprite.y,
letterSprite.width,
letterSprite.height
)
}
};
function LetterSprite(sx,sy,w,h)
{
var self = this;
self.sheetX = sx;
self.sheetY = sy;
self.width = w;
self.height = h;
self.x = 0;
self.y = 0;
};
UPDATE 2
After another 24 hours of crunching I am very close to a solution. with a few minor tweaks to some of the functions:
function convertStringToImages(string)
{
var string = string;
var splitString = string.split("");
var x = 0;//Added x here
var y = 0;//Added y here CAN set y here
for(var s=0; s<splitString.length; s++)
{
if(s !== -1)//If s is not the last element
{
x = s * kearning;
switch(splitString[s])
{
case "A":
var letterSprite = new LetterSprite(1,1,8,10,x,y);//Now with x,y
output.push(letterSprite);
break; //Redacted for brevity
}
}
else
{
x = 0;//This resets fine
y += leading;//This has no effect WHY?
}
}
I can now get all the fragments to render on top of one another in the x direction. Great! now I just have to separate them vertically. This is where I am stuck again. I cannot manipulate the y coordinate after it is set. I have no idea why.
UPDATE 3
I solved it finally. All I needed to do was solve x pre render (as is) and solve y post render like so:
function render(array)
{
context.clearRect(0,0,canvas.width,canvas.height);
var array = array;
var letterSprite;
for(var l=0; l<array.length; l++)
{
letterSprite = array[l];
//letterSprite.x = l*kearning;
//console.log(letterSprite.x);
//letterSprite.y += leading;
context.drawImage
(
art,
letterSprite.sheetX,
letterSprite.sheetY,
letterSprite.width,
letterSprite.height,
letterSprite.x,
letterSprite.y,
letterSprite.width,
letterSprite.height
)
letterSprite.y += leading;//Update y here
}
console.log("X: " + letterSprite.x + ", Y: " + letterSprite.y);
};

Split an array into even chunks based on integer in javascript

This might be a duplicate, though I didn't find any questions specific to my problem here.
Say I have an array like this
var hundred = [1,2,3,4,5...100]
This array has 100 elements. From 1 to 100.
Based on an integer, how can I split this array into another array with the same amount of elements, except they've been evenly distributed like this?
var integer = 2;
var hundred = [50,50,50,50,50,50...100,100,100,100,100,100]
In this example, the array has 50 elements with the value 50, and 50 elements with the value 100, because the integer was 2.
I'm bad at math, so this might be incorrect, but I hope you understand what I mean. The array must have the same ammount of indexes after the calculation.
Edit (Due to me being very bad at formulating questions, I'm going to use the code I need this for here):
So I have a frequencybin array (from the AudioContext analyser):
var fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);
This array has a set number of elements ( They are the frequencies of audio played ).
Now I have a spectrum analyser, which has a set number of "bars" so if I have only 3 bars, then how can I split the fbc_array so that each bar has the evenly distributed frequency in it? For example, with 3 bars, bar one would have the bass, bar two would have the mids, bar three would have the treble.
I'm using a for loop for iterating over each bar:
for (i = 0; i < bars; i++) {
bar_x = i * canspace;
bar_width = 2;
bar_height = -3 - (fbc_array[i] / 2);
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
Here's what I gathered from this craziness! Sorry you're having trouble conveying your problem. That's always a headache! Good luck.
//set integer to whatever you want
var integer = 3;
var randomNumber;
var output = new Array()
function getRandomIntInclusive(min, max) {
randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
}
for(i = 0; i<integer;i++){
getRandomIntInclusive(1,100);
for(j = 1; j< (100/integer); j++){
output.push(randomNumber);
}
}
//note, you will not always get 100 array items
//you can check with console.log(output.length);
console.log(output);
(Written before your update, so guessing here).
You're looking for a way to approximate a graph so that it's divided into bands and each point within a band is replaced with that band's maximum:
Number.prototype.times = function(fn) {
var a = [];
for(var i = 0; i < this; i++)
a.push(fn(i));
return a;
}
function approximate(src, n) {
var res = [],
size = Math.ceil(src.length / n),
i = 0;
while(i < src.length) {
var chunk = src.slice(i, i += size)
var p = Math.max.apply(null, chunk);
// this gives you an average instead of maximum
// p = chunk.reduce((x, y) => x + y) / chunk.length;
res = res.concat(size.times(i => p));
}
return res;
}
src = 20..times(i => 10 + Math.floor(Math.random() * 80));
res = approximate(src, 4);
document.write('<pre>'+JSON.stringify(src));
document.write('<pre>'+JSON.stringify(res));

Javascript - generating random pairs of numbers if pair doesn't already exist

Edit: If you read Matt Bryant's answer, you'll see that it should work but he uses indexOf() method and that method doesn't work with I.E 8 or later and I need it to work on I.E 8. I tried doing this as a work around to the indexOf() method but it's not working.
var tester = -1;
for (var test=0; test<xposition.length; test++) {
if (x == xposition[0]) {
tseter = x;
}
}
Any idea why it doesn't work?
Original question:
So I want to generate random pairs of numbers but only if the pairs of number didn't already be generated. Here is what I tried, hopefully if you read what I tried, you will understand what it is exactly which I need.
function randomPairs() {
var xposition = []; //array which holds all x coordinates
var yposition = []; //array which holds all y coordinates
for (var i=0; i<5; i++) { //do everything below 5 times (generate 5 pairs)
var x = getRandom(1,7); //generate new x point
var y = getRandom(2,7); //generate new y point
if ( jQuery.inArray(x, xposition) ) { //if newly generated x point is already in the xposition array (if it was already previously generated
var location = xposition.indexOf(x) //find the index of the existing x
if (y == yposition[location]) { //if the newly generated y points equals the same y point in the same location as x, except in the yposition array
while ( y == yposition[location]) {
y = getRandom(2, 7); //change y
}
}
}
}
xposition.push(x); //put x into the array
yposition.push(y); //put y into the array
}
So, any idea why it isn't working? Am I using the jQuery.inArray() and the .indexOf() method properly?
Oh, and getRandom is
function getRandom(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
basically, it generates a number between the min and max.
Also, when I tried to do
alert(xposition);
alert(yposition);
it is blank.
The issue is that you are adding x and y to the array outside of the loop. A fix for this (plus a removal of the unneeded jQuery) is:
function randomPairs() {
var xposition = []; //array which holds all x coordinates
var yposition = []; //array which holds all y coordinates
for (var i=0; i<5; i++) { //do everything below 5 times (generate 5 pairs)
var x = getRandom(1,7); //generate new x point
var y = getRandom(2,7); //generate new y point
var location = xposition.indexOf(x);
if (location > -1) { //if newly generated x point is already in the xposition array (if it was already previously generated
if (y == yposition[location]) { //if the newly generated y points equals the same y point in the same location as x, except in the yposition array
while ( y == yposition[location]) {
y = getRandom(2, 7); //change y
}
}
}
xposition.push(x); //put x into the array
yposition.push(y); //put y into the array
}
}
Note that you should probably return something from this function.
If you have to support old browsers, replace the line
var location = xposition.indexOf(x);
with
var location = jQuery.inArray(x, xposition);
One of the main issue with this approach is that you have to think of cases when there are multiple unique pairs with the same x or y value.
x = [1, 1, 1], y = [1, 2, 3]
Note that Array.indexOf only returns the first index when the given element can be found in the array. So you would have to recursively run it beginning from the index you found the match from.
A simple approach of generating a unique pair of integers can be done without jquery:
http://jsfiddle.net/WaFqv/
I'm assuming the order does matter, so the x = 4, y = 3 & x = 3, y = 4 would be considered as two unique pairs.

Categories