Javascript - Error creating objects on the fly - javascript

I am trying to simulate data to a function that would usually receive a JSON parsed data structure. When running this I get an error TypeError: can't convert undefined to object here: data.targets[i] = {
What am I doing wrong?
function SendFakeTargets(maxTargets, interval) {
var data = {};
data.timestamp = +new Date;
var time = data.timestamp * 0.0005;
var x = Math.sin(time) * 192 + 256;
var y = Math.cos(time * 0.9) * 192 + 256;
console.log(x, y);
for (var i = 0; i < maxTargets; i++) {
console.log(i);
data.targets[i] = { //error is here
id: i,
x: x + (i * 10),
y: y + (i * 10)
};
}
HandleTargetData(data);
setTimeout("SendFakeTargets("+maxTargets+", "+interval+")", interval);
}

you should previously declare
data.targets = [];
before using data.targets[i] inside the loop, otherwise data.targets is undefined. In a shorter way you could write
var data = {
timestamp : +new Date,
targets : []
};
And as a side note, never use strings in setTimeout/Interval. Do instead
setTimeout(function() {
SendFakeTargets(maxTargets, interval);
}, interval);

I think you need to initialize the targets array before using it, as it is undefined. You are defining data as
var data = {}
which is declaring it as an empty object, anything else that you're doing with it is added on the fly - arrays need to be initialized before you can call any index in them. I believe what you need to do is:
var data = { targets: [] }

You never declared "data.targets" as a an object so javascript doesn't know how to assign anything to it.
At the top of your code just define "targets":
data.timestamp = +new Date;
data.targets = {} or [];
...

Related

Array(push - object) not working as expected

I'm having issues with pushing object into an array.
I set an object with values and push them to the array. I then change some of the values of the object and push the object into the array again.
However, on inspection, both objects pushed into the array are identical, both object's values are identical to the last object that was pushed into the array.
let ProductPosition = function (x, y) {
this.x = x;
this.y = y;
}
let PalletType = (function () {
function PalletType() {
this.PatternType = '';
this.ProductWidth = 0;
this.PalletWidth = 0;
this.ProductPositions = [];
}
});
function getPalletPositions(pallet, pattern) {
pal.ProductPositions = [];
let posn = new ProductPosition();
switch (pattern) {
case '1U1':
posn = [];
posn.y = pal.PalletWidth / 2;
posn.angle = 0;
posn.apprDir = 0;
pallet.ProductPositions.push(posn);
break;
case '2U1':
posn = [];
posn.y = pal.PalletWidth / 2 + pal.ProductWidth / 2;
console.log('y pos 0 ' + posn.y);
pal.ProductPositions.push(posn);//first push
posn.y = pal.PalletWidth / 2 - pal.ProductWidth / 2;
console.log('y pos 1 ' + posn.y);
pallet.ProductPositions.push(posn);//first push
break;
}
}
let pal = new PalletType();
pal.PalletWidth = 1165;
pal.ProductWidth = 400
let pat = '2U1';
getPalletPositions(pal, pat);
pal.ProductPositions.forEach(function (pos) {
console.log("pos.y:" + pos.y);
});
Actual output:
y pos 0 782.5 <-value of y of first push
y pos 1 382.5 <-value of y of second push
pos.y:382.5 <-should be 782.5
pos.y:382.5
I'd expect:
y pos 0 782.5 <-value of y of first push
y pos 1 382.5 <-value of y of second push
pos.y:782.5
pos.y:382.5
I'm totally baffled and tried a few things, but to no avail.
You were mutating that object you can use spread operator or Object.assign
Check below
let ProductPosition = function (x, y) {
this.x = x;
this.y = y;
}
let PalletType = (function () {
function PalletType() {
this.PatternType = '';
this.ProductWidth = 0;
this.PalletWidth = 0;
this.ProductPositions = [];
}
});
function getPalletPositions(pallet, pattern) {
pal.ProductPositions = [];
let posn = new ProductPosition();
debugger;
switch (pattern) {
case '1U1':
posn = [];
posn.y = pal.PalletWidth / 2;
posn.angle = 0;
posn.apprDir = 0;
pallet.ProductPositions.push(posn);
break;
case '2U1':
posn = [];
posn.y = pal.PalletWidth / 2 + pal.ProductWidth / 2;
console.log('y pos 0 ' + posn.y);
pal.ProductPositions.push({...posn});//first push
posn.y = pal.PalletWidth / 2 - pal.ProductWidth / 2;
console.log('y pos 1 ' + posn.y);
pallet.ProductPositions.push({...posn});//first push
break;
}
}
let pal = new PalletType();
pal.PalletWidth = 1165;
pal.ProductWidth = 400
let pat = '2U1';
getPalletPositions(pal, pat);
pal.ProductPositions.forEach(function (pos) {
console.log("pos.y:" + pos.y);
});
That's because "posn" is an Object, so you are actually pushing a reference to this object rather than a primitive value.
You could, for example, copy the object:
pallet.ProductPositions.push({...posn});
The Spread operator will create a shallow copy.
If you need a deep copy use the following:
pallet.ProductPositions.push(JSON.Parse(JSON.Stringify(posn)));
Pay attention that the JSON method is not able to copy functions.
You're trying to push the same object 2 times in the same array. The first time with some value and next time, you're modifying the value in the same object and pushing in the array. So in total the same object reference is getting modified. As a result, the array has same object added 2 times.
Another way is, you can use slice operator on your array to create new instance of the array and then do the second push. OR create 2 different variables and then push it.
Thank you all for your prompt responses, it is very much appreciated.
Solved by using:
let x;
let y;
let angle;
let apprDir;
and assigning these directly. Then:
pallet.ProductPositions.push(new ProductPosition(x, y, angle, apprDir));
Works a treat and simplified the code.

How to set a variable once a day in Javascript

Coniser a variable list = ["A", "B",...] as list of strings. I want to use a Javascript programm that picks three strings from this list once a day and writes it into a HTML field.
Currently I use
function getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
smallList = getRandom(list, 3);
var htmlTags = [
"tag1",
"tag2",
"tag3"
];
for (var i = 0; i < htmlTags.length; i++) {
document.getElementById(htmlTags[i]).innerHTML = smallList[i];
}
Now this list gets new entries every time I refresh the website. Is there a way that smallList is only set once a day/hour/min/ in a pedriod of time only using javascript?
So you want to:
Pick three values from your list and show them on your web page
Each day, pick three new values to show for the whole day
Everyone who visits the page should see the same values regardless of client
As others have suggested, it would be a better candidate for a server-side task than client-side.
For example, you might have a server page which checks for the existence of a value stored in cache. The cache would be set to 24 hours. If the cache is not available, then a new cache object is created and given a half-life of 24 hours. Inside the cache, you could also store the values you wish to retrieve.
Then, you could retrieve the cache and output the values. The particular implementation of the cache would depend on your server-side language.
OKAY: Via Stored Values (COOKIE, SESSION, LOCAL STORAGE, MEMORY):
per user you'd have to use a cookie, session, or write to local storage in the browser.
for all users you'd have to use a server variable somewhere like a database, file, or memory.
you'd set the value to expire in a day and regenerate if expired. this is the answer you will get from most people because it is the only way they know how to solve this, single set value polled from all locations.
BETTER: Via Deterministic Pseudo-Random Number Generator Seeded with Date:
or if you are really ambitious and don't want to rely on a value that you set somewhere, you could use or write a:
deterministic pseudo-random number generator that you seed off of the date. Since deterministic pseudo-random generators produce reproducible "randoms" from the same seed, seeding the date gives you a unique seed per day, hence a unique random per day.
function RC4(seed) {
this.s = new Array(256);
this.i = 0;
this.j = 0;
for (var i = 0; i < 256; i++) {
this.s[i] = i;
}
if (seed) {
this.mix(seed);
}
};
RC4.getStringBytes = function(string) {
var output = [];
for (var i = 0; i < string.length; i++) {
var c = string.charCodeAt(i);
var bytes = [];
do {
bytes.push(c & 0xFF);
c = c >> 8;
} while (c > 0);
output = output.concat(bytes.reverse());
}
return output;
};
RC4.prototype._swap = function(i, j) {
var tmp = this.s[i];
this.s[i] = this.s[j];
this.s[j] = tmp;
};
RC4.prototype.mix = function(seed) {
var input = RC4.getStringBytes(seed);
var j = 0;
for (var i = 0; i < this.s.length; i++) {
j += this.s[i] + input[i % input.length];
j %= 256;
this._swap(i, j);
}
};
RC4.prototype.next = function() {
this.i = (this.i + 1) % 256;
this.j = (this.j + this.s[this.i]) % 256;
this._swap(this.i, this.j);
return this.s[(this.s[this.i] + this.s[this.j]) % 256];
};
function RNG(seed) {
if (seed == null) {
seed = '' + Math.random() + Date.now();
} else if (typeof seed === "function") {
// Use it as a uniform number generator
this.uniform = seed;
this.nextByte = function() {
return ~~(this.uniform() * 256);
};
seed = null;
} else if (Object.prototype.toString.call(seed) !== "[object String]") {
seed = JSON.stringify(seed);
}
this._normal = null;
if (seed) {
this._state = new RC4(seed);
} else {
this._state = null;
}
}
RNG.prototype.nextByte = function() {
return this._state.next();
};
RNG.prototype.uniform = function() {
var BYTES = 7; // 56 bits to make a 53-bit double
var output = 0;
for (var i = 0; i < BYTES; i++) {
output *= 256;
output += this.nextByte();
}
return output / (Math.pow(2, BYTES * 8) - 1);
};
RNG.prototype.random = function(n, m) {
if (n == null) {
return this.uniform();
} else if (m == null) {
m = n;
n = 0;
}
return n + Math.floor(this.uniform() * (m - n));
};
RNG.$ = new RNG();
Date.prototype.yyyymmdd = function() {
var mm = this.getMonth() + 1; // getMonth() is zero-based
var dd = this.getDate();
return [this.getFullYear(), !mm[1] && '0', mm, !dd[1] && '0', dd].join(''); // padding
};
// Using the Date like so will give you the same random between 40 and 50 for the same day
var rng = new RNG((new Date).yyyymmdd()); rng.random(40, 50);
// Test with dates
var rng = new RNG('20180301'); rng.random(40, 50);
var rng = new RNG('20180302'); rng.random(40, 50);
var rng = new RNG('20180301'); rng.random(40, 50);
Store the list in localStorage or a Cookie. Also store the timestamp.
Use setTimeout(function(){...}, n) to examine the timestamp and update the values as needed.
If the page refreshes or is loaded anew, then perform the check on what is stored. If nothing exists, create your list and set the timestamp. If data does exist, then compare the timestamp and update the list as needed.
If you need the list to be consistent across users, then everything needs to be stored, examined and calculated on the server-side.
localStorage.savedData = {
timestamp: new Date(),
dataList: ['a','b','c']
}
To get the values from localStorage:
// you don't have to create variables, you can just use localStorage.[property] to get compare any value
let ts = localStorage.timestamp; // Date object
let dl = localStorage.dataList; // Array of values
For more information on localStorage see (or search the web) -> https://www.w3schools.com/html/html5_webstorage.asp

Initializing object properties with objects own properties

I get the error:
Uncaught TypeError: Cannot read property 'interval' of undefined
When I try to initialize an object like this:
var loop = {
interval: 5 * 1000,
maxInterval: loop.interval * 12
};
So instead I have to do it like this:
var loop = {
interval: 5 * 1000
};
loop.maxInterval = loop.interval * 12;
Is there a better way of doing this?
No way.
But conceptually all you need is moving the constant one level up:
var defaultInterval = 5000;
var loop = {
interval: defaultInterval,
maxInterval: defaultInterval * 12
};
One option is using self executing function.
var loop = (function () {
var _ = {};
_.interval = 5 * 1000;
_.maxInterval = _.interval * 12;
return _;
})();

How to get size of a DataView type (eg. Uint32=4, Float64=8) to advance the offset?

I'm parsing a serialized object with a DataView and want to be able to increment an offset variable depending on data sizes. I'd rather not redefine variables for simple things like BYTES_PER_UINT16
...
var dv = new DataView(payload);
var offset = 0;
anObject.field1 = dv.getUint8(offset);
offset += BYTES_PER_UINT8;
anObject.field2 = dv.getUint32(offset, true);
offset += BYTES_PER_UINT32;
...
You need to wrap them in an object which does this for you.
For example:
function DataViewExt(o) {
this.view = new DataView(o);
this.pos = 0;
}
DataViewExt.prototype = {
getUint8: function() {
return this.view.getUint8(this.pos++);
},
getUint16: function() {
var v = this.view.getUint16(this.pos);
this.pos += 2;
return v
},
// etc...
};
Now you can create an instance:
var dv = new DataViewExt(payload);
var uint8 = dv.getUint8(); // advances 1 byte
var uint16 = dv.getUint16(); // advances 2 bytes
...
console.log("Current position:", dv.pos);
Modify to fit your scenario.

jQuery .css does not work for all IDs in list

I am trying to dynamically set the css style of some divs which are listed. When I iterate over them, and set their new css values, only the last div will get the new css values.
for (var i = 0, l = list.length; i < l; i++)
{
b = list[i];
b.$block.css({
"top": b.pos.y + "px",
"left": b.pos.x + "px"
});
b.pos = b.pos.add(b.vec);
}
b.$block is the jQuery object. b.pos and b.vec are just some Vector2D instances which I use to calculate the top and left value all divs.
The css() method does affect all the jQuery objects in the collection, but if you run your code in a debugger, you can see that you are passing the same object every time: div#block3.
Your problem lies elsewhere outside of the blockSim(list) method because you are passing a list of the same object.
New solution
You forgot 3 times the keyword new:
return (Vector2D(that.x + other.x, that.y + other.y)); // line 92
return (Vector2D(that.x - other.x, that.y - other.y)); // line 96
metaList.push(createBlock(i)); // line 133
You have to write
return (new Vector2D(that.x + other.x, that.y + other.y)); // line 92
return (new Vector2D(that.x - other.x, that.y - other.y)); // line 96
metaList.push(new createBlock(i)); // line 133
Just with the new keyword the this parameter of the functions is set to a new value. Here the code: http://jsfiddle.net/6eB3U/1/
Old solution
Here you have a debugged example of your code: http://jsfiddle.net/8ET77/
Consider the function createBlock():
function createBlock(idx)
{
var tmp = new Array(4);
for (var i = 0; i < 4; i++) {
if (i < 2)
tmp[i] = Math.round(Math.random() * 1000) % 600;
else
tmp[i] = Math.round(Math.random() * 10) % 4;
}
this.pos = new Vector2D(tmp[0], tmp[1]);
this.vec = new Vector2D(tmp[2], tmp[3]);
this.$block = $("#block"+idx);
return this;
}
For each call on the function, the reference this points to the same object (i.e. the function object createBlock()). The corrected function now looks like this:
function createBlock(idx)
{
var tmp = new Array(4);
for (var i = 0; i < 4; i++) {
if (i < 2)
tmp[i] = Math.round(Math.random() * 1000) % 600;
else
tmp[i] = Math.round(Math.random() * 10) % 4;
}
block = {}; // new object is generated here!
block.pos = new Vector2D(tmp[0], tmp[1]);
block.vec = new Vector2D(tmp[2], tmp[3]);
block.$block = $("#block"+idx);
return block;
}
You have to make the same correction for the function Vector2D().
Can you try to use jQuery $.each function? It is probably solve your problem.
It just may something like that.
$.each(b.$block, function( index, value ) {
$(value).css({"top" , b.pos.x + "px"});
});

Categories