I'm trying to create a few objects whose names are the id attributes of the divs with class="characteristic". How do i get the id for the name?
window.onload = function() {
var characteristicElements = document.getElementsByClassName("characteristic");
for ( var i = 0 ; i < characteristicElements.length ; i++ ) {
var characteristicElements[i].getAttribute("id") = { set: false, val: 0, unn: 0 };
}
};
You should create a map then, using an object, and use the ID as property name:
var objects = {};
var characteristicElements = document.getElementsByClassName("characteristic");
for ( var i = 0 ; i < characteristicElements.length ; i++ ) {
objects[characteristicElements[i].id] = {'set': false, val: 0, unn: 0 };
}
Afaik you have to put set in quotes because it is a keyword in object literals. I could be wrong though. Also note that getElementsByClassName is not available in IE8 and before.
I recommend reading the MDN JavaScript Guide - Working with Objects.
characteristicElements[i].getAttribute("id") is a string. What you're doing is like var "foo" = "bar";, doesn't make sense. What you want to do is doable but only with eval, and eval is evil. If you put the objects within another object it gets much easier:
window.onload = function() {
var characteristicElements = document.getElementsByClassName("characteristic");
var objects = {}; // New empty object
for ( var i = 0 ; i < characteristicElements.length ; i++ ) {
objects[characteristicElements[i].getAttribute("id")] = { set: false, val: 0, unn: 0 };
}
};
objects["foo"] is the same as objects.foo, just another notation.
Related
newbie question:
i am using a simple for loop in my code. Let's say this returns 3 pozitive values of a, b and c. What i want is to have all 3 values described as different variables to use later outside the loop. Is it possible? Sample code:
var CampAdGR = ss.getRange("A9").getValue();
var data = ss.getRange("A23:A").getValues();
for (var i = 0; i < data.length; i++) {
if (data[i][0] == CampAdGR) {*continue code*}
Thanks!
If you have an array of values, you can either declare the variables seperately:
var data = [ 1, 2, 3 ];
var a = data[ 0 ];
var b = data[ 1 ];
var c = data[ 2 ];
console.log( a );
console.log( b );
console.log( c );
Or you could use destructuring to set them in one line:
const data = [ 1, 2, 3 ];
const [ a, b, c ] = data;
console.log( a );
console.log( b );
console.log( c );
If you don't know in advance how many values you'll have inside the data, it's better to just keep working with the entire data array, since transforming the data can be done with array.map(), array.forEach() etc.
You can declare a different variable, usually an array, and use it as a storage.
var CampAdGR = ss.getRange("A9").getValue();
var data = ss.getRange("A23:A").getValues();
var storage = [];
for (var i = 0; i < data.length; i++) {
if (data[i][0] == CampAdGR) {
storage.push(data[i]);
}
}
storage.map( storedValue => {
console.log(storedValue);
});
If you need 3 different storage, declare 3 variables or save them on a single object.
The loop will create new variable names at run time (varName1, varName2, varName3...).
I concatenate the index i with the varName for unique variable names (it seems logic to me because the index is the only value changing throughout the loop).
The output will give you n times new variables (depending on how big your data array is) with their corresponding values.
var CampAdGR = ss.getRange("A9").getValue();
var data = ss.getRange("A23:A").getValues();
for (var i = 0; i < data.length; i++) {
if (data[i][0] == CampAdGR) {
var varName = varName.concat('aName' + data[i]);
}
}
I've read a lot of articles about objects and arrays lately but they had contrary info / facts for some reason. I need your help to learn once and for all: when it's best to use array and when object?
Obvious reason is to use array when you need specific order. What else? What about this example?
There's a lot of pushing
There's a lot of checking if array contains something
There's a lot of removing
Note that this example has much smaller numbers that I need (which is in thousands).
Code I wrote for this question, it has few things I would never do but for the sake of this question:
var x = [];
var y = [];
var z = [];
var clickedX = [];
var clickedY = [];
var clickedZ = [];
var count = 100;
for ( var a = 0; a < count; a++ ) {
//For the sake of this example: random int 1, 2 or 3
var type = Math.floor(Math.random() * 3) + 1;
createDiv( type );
}
function createDiv( thisType ) {
var self = this;
var div = self.div;
var type = thisType;
if( !div ) {
div = this.div = document.createElement( 'div' );
div.className = 'marker';
//Push to "right" array
if( type == '1' ) {
x.push( self );
}
else if ( type == '2' ) {
y.push( self );
}
else {
z.push( self );
}
//Attach click event
div.onclick = function() {
// X
var indexX = x.indexOf( self );
if( indexX > -1 ) {
//Push to new array
clickedX.push( self );
//Remove from old array
x.splice( indexX, 1 );
}
// Y
var indexY = y.indexOf( self );
if( indexY > -1 ) {
//Push to new array
clickedY.push( self );
//Remove from old array
y.splice( indexY, 1 );
}
// Z
var indexZ = z.indexOf( self );
if( indexZ > -1 ) {
//Push to new array
clickedZ.push( self );
//Remove from old array
z.splice( indexZ, 1 );
}
}; // onclick
} // if( !div )
} // createDiv()
Data example what Im currently dealing with:
// Data Im dealing with
objects = {
{ objects1: [
0: { object : [4-5 assigned values (int, string, array)] },
1: { object : [4-5 assigned values (int, string, array)] },
//etc
]
},
{ objects2: [
0: {object},
1: {object},
//etc
]
}
}
// 1. I need to iterate through every "object" in both "objects1" and "objects2"
// 2. I need to iterate through "array" in every "object" in "objects1"
for( /* "object" count in "objects1" */ ) {
//Do something for each "object" in "objects1"
for( /* items in "array" count */ ) {
//Do something for each item in "array"
}
}
// AND
for( /* "object" count in "objects2" */ ) {
//Do something for each "object" in "objects2"
}
Arrays
Basically, you need to use an array, when you have a list of consequent data, and you are going to work with it as a collection.
It easily adds, iterates, gets count and checks for existence.
It is difficult to remove items in an array.
Also, it stores order.
For example, if you have 10 integers, and you need to find the index of the minimum one, then it is logical to use an array.
Objects
Use objects, when your items have keys (especially, non-integer) and you are going to work with them one by one.
It is convenient to add, remove, check for existence properties.
However, it is less convenient to iterate through object properties, and absolutely inproper to get its count.
It is also impossible to store items' order.
Time complexity
Answering your question about pushing, checking for existence and removing:
Both property setting and array pushing takes O(1) time, but I strongly believe that the first one is a bit slower
Checking for existence takes the same O(1) time
Removing an element is what array is not designed for - it needs to shift all items and takes O(n) time, while you can remove a property for O(1)
Bad usage example
There are some really bad usages. If you have the following, then you use array or object wrong.
Assign a random value to an array:
var a = [];
a[1000] = 1;
It will change the length property of an array to 1001 and if you try to output this array, you will get the following:
[undefined,undefined,undefined,undefined... 1]
which is absolutely useless, if it is not done on purpose.
Assign consequent values to an object
var a = {};
for (var i = 0; i < 10; i++)
{
a[i] = 1;
}
Your code
Talking about your code, there are many and many ways to do this properly - just choose one. I would do it in the following way:
var items = [];
for (var i = 0; i < count; i++)
{
var o = {
type: Math.floor(Math.random() * 3) + 1,
isClicked: false
};
var div = document.createElement('div');
div.className = 'marker';
div.onclick = function() {
o.isClicked = true;
};
o.div = div;
items.push(o);
}
function GetDivs(type, isClicked)
{
var result = items;
if (type)
result = result.filter(function(x) { return x.type === type; });
if (isClicked !== undefined)
result = result.filter(function(x) { return x.isClicked === isClicked; });
return result;
}
Then, you will be able to get any items you want:
var all = GetItems(0); // or even GetDivs()
var allClicked = GetItems(0, true);
var allNotClicked = GetItems(0, false);
var divsTypeA = GetItems(1);
var clickedDivsTypeB = GetItems(2, true);
var notClickedDivsTypeC = GetItems(3, false);
With this usage, you don't need to remove any items at all - you just mark them as clicked or not, which takes O(1) time.
jQuery
If you use jQuery and data HTML attributes, then you won't need to use arrays or objects at all:
for (var i = 0; i < count; i++)
{
$('<div/>')
.addClass('marker')
.attr('data-type', Math.floor(Math.random() * 3) + 1)
.appendTo('body')
.click(function() {
$(this).addClass('clicked');
});
}
Now, you can use the selector to find any items:
var all = $('.marker');
var allClicked = $('.marker.clicked');
var allNotClicked = $('.marker:not(.clicked)');
var divsTypeA = $('.marker[data-type='1']');
var clickedDivsTypeB = $('.marker[data-type='2'].clicked');
var notClickedDivsTypeC = $('.marker[data-type='1']:not(.clicked)');
However, the last approach can produce lags if you really have thousands of records. At the same time, is it a good idea to have 1000 dynamic divs on your page, at all? :)
When all else fails run a test, Again it depends on what you intend to use the data structure for, everything is a trade off, performance || efficiency.
Be prepared to wait a few seconds for the tests to finish.
function gimmeAHugeArray( length ){
var arr = [];
for( var ii = 0; ii < length; ii++ ){
arr.push( (ii * 100000 ).toString(16) );
}
return arr;
}
function gimmeAHugeObject( length ){
var obj = {};
for( var ii = 0; ii < length; ii++ ){
obj[ (ii * 100000 ).toString(16) ] = ii;
}
return obj;
}
var hugeArray = gimmeAHugeArray( 100000 );
var hugeObject = gimmeAHugeObject( 100000 );
var key = (8000 * 100000).toString(16);
console.perf(function arrayGetWithIndexOf(){
var val = hugeArray.indexOf( key );
});
console.perf(function objectGetWithKey(){
var val = hugeObject[ key ];
});
console.perf(function arrayIterateAllProperties(){
var val = null;
for( var ii = 0, ll = hugeArray.length; ii < ll; ii++ ){
val = hugeArray[ii];
}
}, 50);
console.perf(function objectIterateAllProperties(){
var val = null,
key;
for( key in hugeObject ){
val = hugeObject[ key ];
}
}, 50);
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
I'm going to answer your question from the comment above, from Solo.
The question is What to do if I need to do both with same data?
I have found that the best approach ( at least for me ) was to create an array, that may have empty nodes, but keep another array that will keep track of the ids that are populated.
It looks something like this:
var MyArray = function()
{
this.storage = [];
this.ids = [];
};
MyArray.prototype.set = function(id, value)
{
this.storage[id] = value;
this.ids[this.ids.length] = id;
};
MyArray.prototype.get = function(id)
{
return this.storage[id];
};
MyArray.prototype.delete = function(id)
{
delete this.storage[id];
};
This solution works quite well with both approaches, because you can delete anything at O(1), because everyone has a given ID, and you iterate it easily, because you have all of the IDs the values are stored at.
I have also created a really small class for my own usage, and it performs VERY well. Here's the link https://github.com/ImJustAskingDude/JavascriptFastArray . Here's a question I asked about these things: Javascript Array Performance .
Please read what I write there, this solution applies to a pretty particular situation, I do not know exactly if it applies to you.
That is why I asked you to read what is in those links I provided, I have written like 10 pages worth of material on this, even though some of it is pure garbage talk about nothing. Anyway, I'll write a couple of examples here as well, then.
Example:
Let us say you had data from the database, the data from the database is pretty much required to have unique integer IDs, but each user may have them spread over a wide range, they are MOST LIKELY not contiguous.
So you get the data from the database into objects, something like this:
var DBData /* well, probably something more descriptive than this stupid name */ = function(id, field1, field2, field3)
{
this.id = id;
this.field1 = field1;
this.field2 = field2;
this.fiedl3 = field3;
};
Given this class, you write all of the data from the database into instances of it, and store it in MyArray.
var data = new MyArray();
// YOU CAN USE THIS, OR JUST READ FROM DOM
$.get(/* get data from DB into MyArray instance */, function(data) { /* parse it somehow */ var dbdata = new DBData(/* fields that you parsed including ID */); data.set(ID, dbdata)});
and when you want to iterate over all of the data, you can do it like this:
Just create a function to create a new array, that has all of the data in a nice contiguous block, it would go something like this:
function hydrate(myarray, ids)
{
var result = [];
var length = ids.length;
if(!length)
{
return null;
}
var storage = myarray.storage;
for(var i = 0; i < length; i++)
{
result[result.length] = storage[ids[i]];
}
return result;
};
I use this method, because you can use it to select any configuration of objects into a contiguous array very easily.
One solution to handle arrays of arrays, would be to just modify MyArray to deal with that exclusively, or make a version of it, that handles it.
A different solution would be to rethink your approach, and maybe add all of the data in a single MyArray, but put all of the
objects1: [
0: { object : [4-5 assigned values (int, string, array)] },
1: { object : [4-5 assigned values (int, string, array)] },
//etc
]
},
{ objects2: [
0: {object},
1: {object},
//etc
]
}
into nice objects.
And seriously, read what I wrote in those links, there's my reasoning for everything there.
i am trying to create new Objects with names out of an array.
Without an array i would do:
var object_bruno = new Object();
var object_carlos = new Object();
var object_luci = new Object();
so i will end up with 3 new Objects. But why wont we do that with an loop, which makes it more easy to adde some more Objects later. So i ttried:
// an array full of object names
var obj_arr = [ "object_bruno", "object_carlos", "object_luci"];
// Method one:
for (x in obj_arr) {
alert(obj_arr[x]); // right names shown
var obj_arr[x] = new Object(); //syntax error, dosent work??
};
// Method two:
obj_arr.forEach(function(func_name) {
alert(func_name); // right names
var func_name = new Object(); // no objects are created ???
});
basicly i would prefer to use Method two. i like it because i can fill them late the same way? hopefuly? Any ideas what wents wrong?
You can just loop over the array and assign a new Object to each of the items , like this:
for (var i = 0, l = obj_arr.length; i < l; i++) {
obj_arr[i] = {};
}
UPDATE
You can also do it in this way by applying properties to the global object, for example window:
var people = [ "object_bruno", "object_carlos", "object_luci"];
for (var i = 0, l = people.length; i < l; i++) {
global[people[i]] = {};
}
Using this solution makes the objects global, so you can use them like object_bruno.
Another improvement can be the usage of computed propertiey names of ECMAScript 2015:
var people = [ "bruno", "carlos", "luci"], prefix = 'object_';
for (var i = 0, l = people.length; i < l; i++) {
global[prefix + people[i]] = {};
}
This allows to have more meaningful array.
Note, the global can be the window object in browsers or global object in NodeJS, or perhaps something else in other environments.
Because you are creating a new variable by declaring
obj_arr.forEach(function(func_name) {
alert(func_name); // right names
var func_name = new Object(); // no objects are created ???
});
try this
obj_arr.forEach(function(func_name) {
alert(func_name); // right names
func_name = new Object(); // no objects are created ???
});
var obj_arr = [ "object_bruno", "object_carlos", "object_luci"];
obj_arr.forEach(function(func_name, index, arr) {
arr[index] = {};
});
In JavaScript, if you declare a constructor like this:
var PMFont = function(text, font, size) {
this.text = text;
this.font = font;
this.size = size;
/*
...
ton of code
...
*/
x = 15;
};
var test = new PMFont('dd', 'Arial', 92);
And you create a global variable like the example above: x = 15;, is there a way to know, once your object has been created, if there are new global variables that have been created?
I've downloaded some code, and I'd like to know if there are some useless variables like in my example that stay in memory. I may run into far worse problems, for example:
imgd = ctx.getImageData(0, 0, this.baseWidth, this.baseHeight)
...gets all the data of an HTML5 canvas 2D context, and if it's not freed it takes a lot of RAM.
Since all JS variables default to undefined, if you say the term assigned, the previous answers are not precise:
function isGloballyDefined(varName) {
return window[varName] !== undefined
}
If a variable has a null or false value, it already has been assigned by some piece of code.
Since you didn't specify the context, depending where you are running this code, the window object might not be available, so if you want to use this in a node context, then you probably want to look for the variable in the global object.
Hope this helps.
Yes. You can check if any object has changed by storing it's map and then testing it later against a new map. This helper will do it for you:
JS
function objectWatch(object) {
var keys = Object.keys(object);
return {
done: function () {
var o = {
created: {},
removed: {}
},
newkeys = Object.keys(object);
if (keys.length === newkeys.length) return;
if (keys.length > newkeys.length) {
for (var i = 0, l = keys.length; i < l; i++) {
if (newskeys.indexOf(keys[i]) === -1) o.removed[keys[i]] = object[keys[i]];
}
}
for (var i = 0, l = newkeys.length; i < l; i++) {
if (keys.indexOf(newkeys[i]) === -1) o.created[newkeys[i]] = object[newkeys[i]];
}
return o;
}
};
}
To use it just create an instance of it passing the object you want to watch, run your code, then call the done() method which returns and object with created and removed maps of what has changed.
check = new objectWatch(window);
x = 1;
y = 1;
console.log(check.done());
---> object: {created: {x: 1, y:1, check:object}, removed:{}}
Fiddle
http://jsfiddle.net/L6Lb35nq/
I have a list of objects all named in this fashion:
var p1 = {};
var p2 = {};
p1.name = "john";
p1.hobby = "collects stamps";
p2.name = "jane";
p2.hobby = "collects antiques";
I know how to loop through p1 and p2 to collect the properties, provided I know how many of these p object literals there are. Here's my problem, I don't always know how many of these p object literals there will be. Sometimes it goes up to p2, sometimes it goes up to p20.
Is there a way to loop through objects if I know they all share the same prefix?
Edit: I can't change how I'm getting the list of objects. It's given to me in that format...
If we make the following assumptions:
The objects are global
The number suffixes are sequential
...then the following works:
for (var i = 1; window["p" + i] !== undefined; i++) {
console.log(window["p" + i]); // loop over each object here
}
You should have them in an Array referenced by a single variable.
var p = [];
p.push({
name:"john",
hobby:"collects stamps"
}, {
name:"jane",
hobby:"collects antiques"
});
Then you'd loop the Array, and enumerate each object...
for( var i = 0; i < p.length; i++ ) {
for( var n in p[i] ) {
console.log( p[i][n] );
}
}
EDIT:
It seems from a comment that these may be arriving as individual variable.
If they're global variables, and if they always have the same p1 naming, then you can access them as properties of the global window object.
var obj;
for( var i = 1; obj = window['p' + i]; i++ ) {
if( typeof obj === 'object' ) {
for( var n in obj ) {
console.log( obj[n] );
}
}
}
This loop will run until a p(n) global returns a falsey value.
So as long as a truthy value is found, and its typeof is 'object', you'll iterate that object.
If you have all your data stored in a variable , or a few variables you can push it into the array.
var data = "....JSON";
var a = [];
a.push(data);
Push keeps adding stuff into the array in basic sense.
You could also pop to remove the last pushed data.
Take a look at the other methods here:
http://www.w3schools.com/jsref/jsref_obj_array.asp
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array
Why don't you just store them all in one top-level object literal? It will make it easier to enumerate through them.
EG:
var MyObj = {
p1: {},
p2: {}
};
etc..
[edit]
If they are local vars, are you can't change the format of this data, you might have to use eval.
Don't shoot me:
var p1 = {};
var p2 = {};
p1.name = "john";
p1.hobby = "collects stamps";
p2.name = "jane";
p2.hobby = "collects antiques";
var found = true, c = 1;
while(found) {
try {
var obj = eval('p' + c);
c++;
console.log(obj);
} catch(e){
found = false;
}
}
I don't suggest using this, I suggest changing the format of the data you are receiving, but this is one possible solution.