How do you get the number of keys in a JSON object? - javascript

I am creating a gallery plug-in in which I need to figure out the number of elements in a plain javascript object. The following is how I would like to be able to create the gallery.
$.Gallery.create($('#galContainer'), {
'img1.png': 'http://linktosomewhere/',
'img2.png': 'http://linktosomewhere/',
'img3.png': 'http://linktosomewhere/',
........
}, optionsObj);
This will put the images into the gallery with the corresponding links to some page when clicked. Currently I am using an array for that second parameter without the links and I am getting the length of the images using images.length. However using the notation above would be ideal for me and I need to be able to tell how many keys there are in this object.
I am aware of this post and a few others saying you can not get the number of elements in the object. If this is indeed the case, do you have any suggestions on another way to setup this function call which will be efficient and easy to use?

The code you see in the other question you linked to is really the only way to do it. Here's how you can create a function to do it:
function count(obj) {
var i = 0;
for (var x in obj)
if (obj.hasOwnProperty(x))
i++;
return i;
}

Can't you use an array of objects instead?
$.Gallery.create($('#galContainer'), [
{src:'img1.png', href:'http://linktosomewhere/'},
{src:'img2.png', href:'http://linktosomewhere/'},
{src:'img3.png', href:'http://linktosomewhere/'},
........
], optionsObj);
You should just add a .src and .href in your code to read it.
The way you designed your dataset as a simple hash is not very flexible for additional attributes(size, categories, selected, etc...)

Underscore.js has a method that will tell you how large the object is _.size(obj);

Object.prototype.count = function()
{
var c = 0;var i;
for(i in this){if (this.hasOwnProperty(i)){c++;}};
return c;
}
Personally I would build your own prototype for Object, you can use MyObject.length but I think its not fully supported by IE.
test reveal that the length variable is unavailable in Objects.
Testcase:
MyObject = {
a : '0',
b : '1',
c : '2'
}
if(MyObject.count() > 5)
{
$.Gellery.Error('Images','Only 5 images allowed'); //...
}
http://jsfiddle.net/b9Nwv/

Related

Trying to get the"name" of a javascript object supplied by an API

I am calling an API which is giving me back, among other things, an array of javascript objects. The objects in the array are named and I need to use the name in the new individual objects I am creating from the array. Problem is, I don't know how to get to the object's name.
{
"OldCrowMine.E9001":{"last_share":1524883404,"score":"0.0","alive":false,"shares":0,"hashrate":0},
"OldCrowMine.S9001":{"last_share":1524,"score":"648.24","alive":true,"shares":632,"hashrate":14317274},
}
I am after the "OldCrowMine.E9001" bit. I am sure this is quite simple, I just don't know how to search for the answer because I am not sure what to call this. I have tried searching for a solution.
Just loop - or am I missing something? Simplified raw data version.
var raw = {
"OldCrowMine.E9001":{"share":1524883404},
"OldCrowMine.S9001":{"share":1524}
};
for(var first in raw) {
console.log(first +" share -> "+ raw[first]["share"]);
}
var obj = {
"OldCrowMine.E9001":{"last_share":1524883404,"score":"0.0","alive":false,"shares":0,"hashrate":0},
"OldCrowMine.S9001":{"last_share":1524,"score":"648.24","alive":true,"shares":632,"hashrate":14317274},
}
console.log(Object.keys(obj)[0]);
Get the keys and map the name and the object:
var x= {
"OldCrowMine.E9001":{"last_share":1524883404,"score":"0.0","alive":false,"shares":0,"hashrate":0},
"OldCrowMine.S9001":{"last_share":1524,"score":"648.24","alive":true,"shares":632,"hashrate":14317274},
};
var mapped = Object.keys(x).map(function(d,i){return [d,x[d]]});
The name is map[n][0] and its object is map[n][1] where n is your item number.

Saving Javascript object

I have tree of javascript objects. Let's call it "family. It can contain any number of certain objects ("parents") which can each contain any number of "child" objects. The number of levels in this structure is known and each level of the tree only contains objects of one certain type.
All the objects have data and methods.
I want to save the structured data in the databese. JSON.stringify() does it perfect extracting the data and also saving the structure. But how to get back to objects? JSON.parse() fails, because it recreates the object without methods.
What should I do in this case? Should I write my own function for recreating the object from string? Or should I save the data together with methods somehow (seems a waste).
As I know the structure, it would be very handy if there would be a possibility to point to an object and tell "that's a parent object" and it would get the methods. I could easily cycle through it then. But I don't know how to that and I'm also afraid that my constructors could set some values to the default ones.
The objects constructors would look something like this:
function lines()
{
this.lines = [];
this.height = 0.5*theMargin;
this.addLine = addLine;
function addLine(newline)
{
this.lines.push(newline);
this.height += newline.height;
}
}
function aLine()
{
this.dots = [];
this.height = 0;
this.length = indent;
this.insertDot = insertDot;
function insertDot(pos,newDot)
{
this.dots.splice(pos,0,newDot);
this.length += newDot.length;
this.height = Math.max(this.height,newDot.height);
if (this.length > maxLineLength)
{ "I will not go into details here" }
}
}
Then I would do like:
var a = new lines();
var testline = new aLine();
var testdot = new aDot();
testdot.height = 10;
testdot.length = 15;
testline.insertDot(0,testdot);
a.addLine(testline);
a.addLine(testline);
Then I want to save the data about lengths and heights. And the structure, to know which dot belongs in which line.
I send that data to the webserver. I think these are the key lines to understand the used approach:
post = "name=" + name + "&tab=" + JSON.stringify(file);
req.open("POST", "saveFile.php", true);
req.send(post);
The saved file saves exactly what I wanted - the structure and data. But I don't know how to make it become an object again. I am not insisting to use JSON.stringify() method. I would enjoy any approach that would let me save the content without repeatedly saving the methods.
If you are really hooked on the idea of saving the entire object for some reason then I suggest you use the toString() method of which will essentially return the code body of a function in the form of a string when called on a function.
var obj = { func: function() { alert('hello!'); };
for(var key in obj)
if (typeof obj[key] === 'function')
alert(obj[key].toString());
You would just have to add code to serialize and store this information in addition to the json data.
All that said, you really should be simply storing the state of your objects and reloading them into your application.
EDIT: Reconstructing the object client-side
Disclaimer: I am not a PHP guy so you will be left to finding an actually coding example but I'm confident there is one out there with the power of the almighty Google.
You simply need to use your serializing/deserializing class to serialize the data back into your object.
So imagine the section of pseudo code is the php file for the particular page in question:
<?php
<html>
<head>
<script type="text/javascript">
var model = /* Use serializing class on your server-side object */;
//Now you just need to build a function into your objects that is much like a constructor that can receive this model and rebuild the object
function rebuildObject(yourObject, jsonModel) {
//assign first property
//assign second etc...
}
</script>
</head>
<body>
</body>
</html>
?>
You are essentially templating the json data back to the page in a script tag so you can access it client-side. The javascript interpretter will automatically convert the json into an actual js object that your code can use so no issue there.
In the end I chose the straightforward way to recreate all the objects and copy the data. It turned out to be shorter and nicer than I had imagined before. In case it is useful for anyone else, here's how I did it:
data = JSON.parse(file);
a = new lines();
a.height = data.height;
for (var i=0; i<data.lines.length; i++)
{
a.lines.push(new aLine());
a.lines[i].height = data.lines[i].height;
a.lines[i].length = data.lines[i].length;
for (var j=0; j<data.lines[i].dots.length; j++)
{
a.lines[i].dots.push(new aDot());
[... and so on ...]
}
}

how can I assign object properties to dynamic elements based on the modulus operator

I have created a mini template that holds and <h2> & <h3>, I have created a loop that adds .inner everytime i%2 === 0, what i would like to do is split up the data being outputted to follow the same kind of rule so when i%2 === 0 add .inner and also split the object properties so each .inner should display 2 templated object properties. Can anyone advise on how this can be achieved? Also my templated items seem to return undefined?
Demo here http://jsbin.com/otirax/13/edit
Your code can be improved in many ways. The selector here:
$('.inner').append(temp);
should actually be $(".inner:last"), otherwise it affects all ".inner" objects created so far. Better yet, save a newly created div in a variable:
inner = $('<div class="inner"></div>').appendTo($('#ctn'));
and then simply use the var, thus saving an expensive selector operation:
inner.append(...);
Another improvement is to automate template rendering. Consider the following function: it populates arbitrary {{tags}} from an object:
function render(template, values) {
return template.replace(
/{{(\w+)}}/g,
function($0, $1) { return values[$1] });
}
Complete example: http://jsbin.com/iviziz/2/edit
. Let us know if you have questions.
Templated items seem to return undefined:
var temp = template.replace(/name/ig, data.name)
.replace(/age/ig, data.age);
Should be as below due to them being an array.
var temp = template.replace(/name/ig, data[i].name)
.replace(/age/ig, data[i].age);
I assume that thesonglessbird's reply was clear enough, but just to give a proper answer, instead of
var temp = template.replace(/\{\{name\}\}/ig, data.name)
.replace(/\{\{age\}\}/ig, data.age);
you would need to have
var temp = template.replace(/\{\{name\}\}/ig, data[i].name)
.replace(/\{\{age\}\}/ig, data[i].age);
you can see an example here http://jsbin.com/otirax/16/edit
Aside of that, schemantically a 'template' should not be placed inside script tags as it isn't a script.

Better way of splitting and assigning many values in Javascript?

I have a for loop that cycles through the number of elements that the user has created. There are a lot of available settings in this plugin, and each element can receive it's specific settings.
User settings are entered in the following format: speed_x: "1000,500 > 1000,200 > 0,0"
This controls the speed_x in/out for 3 separate elements. The > divides by object and the commas separate the in/out.
So I can grab specific object speed_x values, I've split speed_x into speed_x_set (splitting by >) resulting in:
1 1000,500
2 1000,200
3 0,0`
3 Inside the loop, I grab the value by index (since it's the object #) and split it by comma (to get speed_x_in and speed_x_out.)
for(var i=0; i<OS.numberofobjects; ++i){
OS.speed_x_on_set[i]=speed_x_set[i].split(",")[0],
OS.speed_x_off_set[i]=speed_x_set[i].split(",")[1],
...
};
Everything is assigned by object and by setting in/out correctly into the master OS settings object. T*he problem is I have many, many settings which need to be split in this fashion...* for example: delay_x_set, speed_y_set, opacity_set, etc. Their names are all based on the default setting name, with "_set" added as shown above. Hopefully this provides enough information. Thanks!
I would avoid to access to the same item twice and perform the same split twice for each iteration. So, you could have something like:
for (var i = 0, item; item = speed_x_set[i++];) {
var values = item.split(",");
OS.speed_x_on_set.push(values[0]);
OS.speed_x_off_set.push(values[1]);
}
Notice that in JavaScript 1.7 (Firefox) you can simply have:
for (var i = 0, item; item = speed_x_set[i++];) {
var [on, off] = item.split(",");
OS.speed_x_on_set.push(on);
OS.speed_x_off_set.push(off);
}
And hopefully in the next version of ECMAScript as well.
It's called "destructuring assignment".
I would say to cache the split result
for(var objindex=0; objindex<OS.numberofobjects; ++objindex){
var splits = speed_x_set[objindex].split(","); //Cache the split so its does not need to be done twice
OS.speed_x_on_set[objindex] = splits[0];
OS.speed_x_off_set[objindex] = splits[1];
...
};
What you're looking for is called parallel assignment, but unfortunately, JavaScript doesn't have it.
In ruby, however, it is common to see similar patterns:
first, second = "first second".split
As others have noted, the obvious way would be to cache split results and assign them separately. Sorry for not answering your question directly.

JavaScript/JQuery: use $(this) in a variable-name

I'm writing a jquery-plugin, that changes a css-value of certain elements on certain user-actions.
On other actions the css-value should be reseted to their initial value.
As I found no way to get the initial css-values back, I just created an array that stores all initial values in the beginning.
I did this with:
var initialCSSValue = new Array()
quite in the beginning of my plugin and later, in some kind of setup-loop where all my elements get accessed I used
initialCSSValue[$(this)] = parseInt($(this).css('<CSS-attribute>'));
This works very fine in Firefox.
However, I just found out, that IE (even v8) has problems with accessing the certain value again using
initialCSSValue[$(this)]
somewhere else in the code. I think this is due to the fact, that I use an object ($(this)) as a variable-name.
Is there a way arround this problem?
Thank you
Use $(this).data()
At first I was going to suggest using a combination of the ID and the attribute name, but every object might not have an ID. Instead, use the jQuery Data functions to attach the information directly to the element for easy, unique, access.
Do something like this (Where <CSS-attribute> is replaced with the css attribute name):
$(this).data('initial-<CSS-attribute>', parseInt( $(this).css('<CSS-attribute>') ) );
Then you can access it again like this:
$(this).data('initial-<CSS-attribute>');
Alternate way using data:
In your plugin, you could make a little helper function like this, if you wanted to avoid too much data usage:
var saveCSS = function (el, css_attribute ) {
var data = $(el).data('initial-css');
if(!data) data = {};
data[css_attribute] = $(el).css(css_attribute);
$(el).data('initial-css', data);
}
var readCSS = function (el, css_attribute) {
var data = $(el).data('initial-css');
if(data && data[css_attribute])
return data[css_attribute];
else
return "";
}
Indexing an array with a jQuery object seems fishy. I'd use the ID of the object to key the array.
initialCSSValue[$(this).attr("id")] = parseInt...
Oh please, don't do that... :)
Write some CSS and use the addClass and removeClass - it leaves the styles untouched afterwards.
if anybody wants to see the plugin in action, see it here:
http://www.sj-wien.at/leopoldstadt/zeug/marcel/slidlabel/jsproblem.html

Categories