I'm trying to create a function to handle purchase of the upgrades. Targeting particular upgrade will by done by passing object as parameter to the function (up) and value of upgrade (amount).
This is my player object:
var player = {
tech: 0,
energy: 0,
upgrades:{
engi5Perc: 0,
engi25Perc: 0,
andro5Perc: 0,
andro25Perc: 0,
robot5Perc: 0,
robot25Perc: 0
}
}
This is my function:
function buyUpgrade(techcost, energycost, up, amount){
if(techcost <= player.tech && energycost <= player.energy){
player.tech -= techcost;
player.energy -= energycost;
up = amount;
console.log("Done, upgrade purchased");
}
}
My HTML:
<button type="button" onclick="buyUpgrade(1, 1, 'player.upgrades.engi5Perc', 0.005 )">Buy 5%</button>
There must be some sort of simple error in my function, and I spent good amount of time trying to figure it out. So far with no luck.
You're currently assigning the amount to the string 'player.upgrades.engi5Perc'. This doesn't work.
But you can use square brackets to address some object's property, whose name you only know at runtime, e.g. obj[propertyName] = value.
Here are some more examples to show the difference:
var obj = {foo: 'bar'};
var propertyName = 'foo';
// the following two assignments are essentially equal:
obj.foo = 'newBar';
obj[propertyName] = 'newBar';
// while this one is obviously assigning 'newBar' to the wrong property:
obj.propertyName = 'newBar';
// and this is what happens in your function:
'obj.foo' = 'newBar';
Try this:
function buyUpgrade(techcost, energycost, up, amount){
if(techcost <= player.tech && energycost <= player.energy){
player.tech -= techcost;
player.energy -= energycost;
player.upgrades[up] = amount;
console.log("Done, upgrade purchased");
}
}
and invoke that function with just the name of the upgrade:
buyUpgrade(1, 1, 'engi5Perc', 0.005 )
Related
The function reverseArrayInPlace(array) is as per solution in eloquentJS. The reverseArrayInPlace function works, by altering the arrayValue as expected. A similar function written for a single variable does not work as expected. In the code, x should come out as 25, but comes out as 20.
//function from eloquentJS solutions, working as expected
`` function reverseArrayInPlace(array) {
for (let i = 0; i < Math.floor(array.length / 2); i++) {
let old = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = old;
}
return array;
}
let arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// working as expected, returns → [5, 4, 3, 2, 1]
// Writing similar function for a single variable
function changeInPlace(a) {
a += 5;
return a;
}
let x = 20;
changeInPlace(x);
console.log(x);
// Not working as expected returns, 20 instead of 25
`
Snippet of the code
Assign changeInPlace to a variable at second last line, and then print that variable in console. It should work.
The problem is that you are returning your calculated variable and not logging it. The one you are logging remains 20.
You must use your function as below :
let variable = changeInPlace(x)
Full code must be like :
function changeInPlace(a) {
a += 5;
return a;
}
let x = 20;
x = changeInPlace(x);
console.log(x);
// x should come out as 25
If for any reason you want to edit a variable value directly, you must access it globally like this:
function changeInPlace() {
x += 5;
return x;
}
let x = 20;
changeInPlace(x);
console.log(x);
For each time you want to change the value in a variable, it needs to be called out again in the form of a declaration. Therefore, output of your function needs to be declared as new value for variable x. Like below:
Your function
// Writing similar function for a single variable
function changeInPlace(a) {
a += 5;
return a;
}
let x = 20;
changeInPlace(x);
console.log(x);
// x should come out as 25
Mine
// Writing similar function for a single variable
function changeInPlace(a) {
return a += 5; // Made a small tweak to your function,
}
let x = 20;
x = changeInPlace(x); // this stores the output of the function in the same variable
console.log(x);
This should give you the desired output, assume this is what you've been asking for if otherwise let me know
Answered by Marijn (author of eloquentJS):
You can't change numbers in JavaScript, only create new ones, so there's no way for a function to change the value of a binding whose value is passes as argument to that function. See for example this section in the book: https://eloquentjavascript.net/04_data.html#h_C3n45IkMhg
I am currently trying to make a program where the text changes as the phone moves every couple value(s) using the P5.JS deviceMoved() function.
(the gif below displays how i wanted the text to change eventually as the device moved)
As seen on the code below, I've put all the text in the array and I wanted to change the index to +1 each time say the move value ads 30 and repeat until all the text is gone.
let button;
let permissionGranted = false;
let nonios13device = false;
let cx, cy
let value = 0;
var myMessages = ["The", "Quick", "Brown", "Fox", "Jumped", "Over", "The", "Lazy", "Dog"];
var index = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(255)
text(myMessages[index], width / 2, height / 2);
fill(value);
text(value, width / 3, height / 3);
textSize(30)
}
function deviceMoved() {
value = value + 5;
if (value > 255) {
value = 0;
}
}
function onMove() {
var currentValue = value + 30;
if (value = currentValue) {
index++;
return;
}
if (index >= myMessages.length) {
index = 0;
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.1/lib/p5.js"></script>
I think my problem is within the onMove function, where I need to define the current value and what values could change the text, I'm fairly new at this so any insight/solution to do this would be highly appreciated :)
Thank you!
There are several issues related to the onMove function. First and foremost it is never called, and unlike deviceMoved it is not a special function that p5.js automatically invokes. Additional issues:
function onMove() {
// You create a currentValue variable that is just value + 30.
// Within the same function, checking if value is >= currentValue,
// assuming that is what you intended, will be fruitless because it
// is never true.
// What you probably want to do is declare "currentValue" as a global
// variable and check the difference between value and currentValue.
var currentValue = value + 30;
// This is the assignment operator (single equal sign), I think you meant
// to check for equality, or more likely greater than or equal to.
if (value = currentValue) {
index++;
// You definitely do not want to return immediately here. This is where
// you need to check for the case where index is greater than or equal
// to myMessages.length
return;
}
if (index >= myMessages.length) {
index = 0;
}
}
Here's a fixed version:
function deviceMoved() {
value = value + 5;
if (value > 255) {
// When value wraps around we need to update currentValue as well to
// keep track of the relative change.
currentValue = 255 - value;
value = 0;
}
onMove();
}
let currentValue = 0;
function onMove() {
if (value - currentValue >= 30) {
// Update currentValue so that we will wait until another increment of
// 30 before making the next change.
currentValue = value;
index++;
// We only need to make this check after we've incremented index.
if (index >= myMessages.length) {
index = 0;
}
}
}
In order to test this out on my mobile device (iOS 14) I had to add some code to request access to the DeviceMotionEvent, and host it in an environment using HTTPS and not embedding in an iframe. You can see my code on glitch and run it live here.
I'm writing analytics and I have to initialize counter counts for (keys) hours, days, weeks, years so as to get frequency of user activity. I need to create a hit count for respective time and increment accordingly. Visits are fed via a loop.
I have this working but I'm not sure if the code below is ideal to do so.
if(!analytics.users[message.user].counts.hourly[hour]) {
analytics.users[message.user].counts.hourly[hour] = 0;
}
analytics.users[message.user].counts.hourly[hour] += 1;
if(!analytics.users[message.user].counts.daily[day]) {
analytics.users[message.user].counts.daily[day] = 0;
}
analytics.users[message.user].counts.daily[day] += 1;
...
I've tried the x = x + 1 || 0 method but that hasn't worked.
Also, is there a way I can set up a function for this?
You could use a function which take the object and the key and perfoms the check and update.
function increment(object, key) {
if (!object[key]) object[key] = 0;
++object[key];
}
Call with
increment(analytics.users[message.user].counts.hourly, hour);
I've tried the x = x + 1 || 0
You almost got it. It should either be:
x = x || 0;
x++;
Or
x = x + 1 || 1;
So, change your code to:
analytics.users[message.user].counts.hourly[hour] =
(analytics.users[message.user].counts.hourly[hour] + 1) || 1
If analytics.users[message.user].counts.hourly[hour] is undefined, the increment operation returns NaN. This is a falsy value. So, it takes 1
You can create a simple increment function like the one below. It first checks for the key to be initialized and if not, it will initialize it to 0. The next line with the increment is safe to execute since the key was previously created.
let message = {
user: "user"
}
let analytics = {
users: {
"user": {
counts: {
}
}
}
}
function incrementAnalytics(analytics, period) {
analytics[period] = analytics[period] || 0;
++analytics[period];
}
let test = analytics.users[message.user].counts;
incrementAnalytics(test, "hourly");
incrementAnalytics(test, "hourly");
incrementAnalytics(test, "daily");
console.log(test);
Cheers!
Can I reduce multiple variables in JavaScript?
For example if I have 3 variables:
var foo = 10;
var boo = 15;
var lol = 15;
//Normal syntax:
foo -=1; // -> foo will be 9
boo -=1; // -> boo will be 14
lol -=1; // -> lol will be 14
Is there an option to do it with one line? or better syntax?
For example: foo ,boo ,lol -=1;
No, you cannot. Standard JavaScript does not give this opportunity. The worse thing that you nearly cannot do it functionally as well. lol -=1; or lol--; is just a shortcut for lol = lol - 1;. So if you will try to write a function, which does that for you e.g.:
function reduce() {
for (var i = 0; i < arguments.length; i++) {
arguments[i] = arguments[i] - 1;
}
}
and then call it like
reduce(foo, bar, lol);
it just won't work because you pass primitive numbers (not references). Every time you change the number inside of the function it won't change the number itself but it will return a new number instead.
This could be solved by using some object to store all the variables, e.g.:
var vars = {
foo: 12,
bar: 13,
lol: 14
};
function reduce(variables) {
for (variable in variables) {
if (variables.hasOwnProperty(variable)) {
variables[variable] -= 1;
}
}
return variables;
}
reduce(vars);
But this is not a list of 3 variables, this is kind of a context you attach them to.
If you do the stuff in a global scope (e.g. in a window without wrapping the stuff in a function), you can combine both ways above into one (Window stores all var-declared variables inside):
function reduce(vars) {
var varslist = vars.split(',');
for (var i = 0; i < varslist.length; i++) {
window[varslist[i]] -= 1;
}
}
reduce('foo,boo,lol');
but as soon as you move it to some subcontext it won't work any longer. Also it looks very nasty. I would rather prefer the second solution with vars object representing your variables context.
You can do it with one line, but you still have to repeat the operation:
foo--, bar--, lol--;
Read about the comma operator. It can be useful, but it's usually not very readable.
I wouldn't even combine var statements into 1:
var foo = 1, bar = 2, lol = 3;
because if a var changes, the entire line changes.
I used to do this:
var foo = 1,
bar = 2,
lol = 3;
but that's bad practice too, because deleting foo or lol will change more than just 1 line (because the var prefix or ; suffix).
Sometimes verbosity is good:
var foo = 10;
var boo = 15;
var lol = 15;
foo -= 1;
boo -= 1;
lol -= 1;
i'm getting the error
Uncaught TypeError: Cannot set property '0' of undefined
for some reason in this line
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
Why is this happening?
thanks for any help
var x_world_map_tiles = 100;
var y_world_map_tiles = 100;
var world_map_array = new Array(x_world_map_tiles);
for (i=0; i<=2; i++)//create a two dimensional array so can access the map through x and y coords map_array[0][1] etc.
{
world_map_array[i]=new Array(y_world_map_tiles);
}
for (i=0; i<=x_world_map_tiles; i++)//just a test
{
for (z=0; z<=y_world_map_tiles; z++)//just a test
{
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
}
}
Arrays in JavaScript have quirks of their own that you may not be expecting if you come from other languages. Two important ones for your use case are:
You cannot directly declare multidimension arrays in JavaScript.
There's little efficiency benefit (and no added safety) when you set the size of the array at creation.
Unlike other languages, JavaScript won't allocate a block of memory for the full array.
(It doesn't know what kind of objects you're going to be putting in each cell,
and therefore how much total memory it will need.)
Instead, all the size argument to Array() does for you is set the array's length property.
For the general, 2d array case, I'd suggest:
Create the "top" array, e.g.:
var i // the first-order index in a
, j // the second order index in a
, a = []
Initialize array elements as needed.
This is called lazy initialization,
and, in this case, it simply involves testing that a[i] exists
before we try to assign something to a[i][j], e.g.:
if (!a[i]) a[i] = []
In English the above statement reads:
"If the i-th element of a is 'falsy', assign an empty array to the i-th element."
Finally, assign the actual value to the multideminsional array:
a[i][j] = 'whatever'
For your case, you know the values ahead of time,
so you can initialize each element in advance.
(If you're not overriding most of the elements, however,
a lazy implementation may be better; see below.)
var x, x_length = 100
, y, y_length = 100
, map = []
// Don't be lazy
for (x = 0; x < x_length; x++) {
map[x] = []
for (y = 0; y < y_length; y++) {
map[x][y] = 'grass.gif|ongrass.gif|collision.gif|above.gif'
}
}
As some others have said,
an array with 100 elements has indexes numbered from zero to ninety-nine,
so a less-than comparison is most appropriate here.
For reference, here's an implementation that uses lazy initialization.
I've gone with a function interface instead of directly accessing the array;
it's longer and more complex, but also more complete.
The initialization pattern I've used here is called an
immediately invoked function expression.
If you haven't seen it before,
it's one of the more useful JavaScript patterns
and well worth taking some time to understand.
var map = (function (x_length, y_length, v_default, undefined) {
// Unless v_default is overwritten, use ...
v_default = v_default || 'grass.gif|ongrass.gif|collision.gif|above.gif'
// Private backing array; will contain only values for a[x][y]
// that were explicitly set.
var a = []
// Private helper function.
// - Returns `true` if `x` is between `0` and `x_length - 1`
// and `y` is between `0` and `y_length - 1`.
// - Returns `false` otherwise.
function valid (x, y) {
return (x >= 0
&& x < x_length
&& y >= 0
&& y < y_length)
}
// Private helper function.
// - Returns `true` if a[x][y] has been set().
// - Returns `false` otherwise.
function exists (x, y) {
return !!a[x] && !!a[x][y]
}
// Private getter
// - Returns the value of a[x][y] if it has been set().
// - Returns `undefined` if the point (x,y) is invalid.
// - Returns `v_default` otherwise.
function get (x, y) {
if (!valid(x, y)) return undefined
else if (exists(x, y)) return a[x][y]
else return v_default
}
// Private setter
// - Returns the value set on success.
// - Returns `undefined` on failure
function set (x, y, v) {
if (valid(x, y)) {
// We're being lazy
if (!a[x]) a[x] = []
a[x][y] = v
return a[x][y]
}
return undefined
}
// Return an interface function.
// - Pass the function three arguments, (x, y, v), to set a[x][y] = v
// - Pass the function two arguments, (x, y), to get a[x][y]
return function (x, y, v) {
if (arguments.length > 2) {
return set(x, y, v)
} else {
return get(x, y)
}
}
})(100, 100)
When I ran the above in node, the following tests printed sensible values:
// Invalid invocations
console.log('map() : %s', map())
console.log('map( 0) : %s', map(0))
console.log('map( -1, 0) : %s', map(-1,0))
console.log('map( 0, -1) : %s', map(0, -1))
console.log('map( -1, -1) : %s', map(-1, -1))
// Valid invocations
console.log('map( 0, 0) : %s', map(0, 0))
console.log('map( 99, 99) : %s', map(99, 99))
console.log('map( 1, 1) : %s', map(1,1))
console.log('map( 1, 1, "foo") : %s', map(1,1, 'foo'))
console.log('map( 1, 1) : %s', map(1,1))
var x_world_map_tiles = 100;
var y_world_map_tiles = 100;
var world_map_array = new Array(x_world_map_tiles);
for (i=0; i<=2; i++)//create a two dimensional array
{
world_map_array[i]=new Array(y_world_map_tiles);
}
for (i=0; i<x_world_map_tiles; i++)
{
for (z=0; z<y_world_map_tiles; z++)
{
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
}
}
As your array has a length of 100, you must go from 0 to 99 (<100) and not to 100 (<=)
This
for (i=0; i<=2; i++)
must be:
for (i=0; i<=x_world_map_tiles ; i++)
You're feeding the world_map_array[i] expression a value for i that does not exist in
world_map_array. So I guess x_world_map_titles is > 2.
I think you need to rewrite i<=2 to i<=x_world_map_titles
Also you do not need to specify the size of the array. I would just use literals in this case:
var x_world_map_tiles = 100;
var y_world_map_tiles = 100;
var world_map_array = [];
for (i=0; i<=x_world_map_tiles; i++)
//create a two dimensional array of 101x101 so can access the map through x and y coords map_array[0][1] etc. {
world_map_array[i]=[];
}
for (i=0; i<=x_world_map_tiles; i++)//just a test {
for (z=0; z<=y_world_map_tiles; z++)//just a test {
world_map_array[i][z]="grass.gif|ongrass.gif|collision.gif|above.gif";
}
}
Getting Uncaught TypeError while using 2-D array in javascript.
For two- dimension array, first declare parent array
var arryTwoDimension= [];
Then depending on the situation, we can create child array by
arryTwoDimension[i]=[] i will be from 0,1,2......
This will solve the issue.