Related
I feel like the answer to this is a hard no in most languages I've used other than PHP, which at least used to have some odd corner cases with stuff like $someArray['nonexistentKey']++.
I'm basically looking to write a sparse object where each property is a numeric counter. The counters should return 0 if undefined and should automatically define themselves with a value of 0 if you try to increment them.
In other words, I want to override the generic Object getter to return 0 in all cases where it would return undefined... or perhaps define the property right there during the get and set it to 0.
So in theory, an overload for ANY property not yet defined would get initialize it at zero. For example this:
myObj['hugePrimeNumberToBaseXToString']++;
would then make it 1.
In the olden days I feel like some way with Object.__proto__ might have worked for this case...
I think what you want is a Proxy.
You can use a proxy to intercept property gets and return your own logic. In this case, return zero in the case of an undefined property.
// Hold the data
const target: { [key in string]: number } = {}
// Access the data
const proxy = new Proxy(target, {
get(target, prop, receiver) {
if (typeof prop === 'string') {
if (target[prop] === undefined) return 0
return target[prop]
}
return undefined
}
})
proxy['hugePrimeNumberToBaseXToString']++
console.log(proxy['hugePrimeNumberToBaseXToString']) //=> 1
Playground
Proxy is definitely the right answer, but I'd argue that tinkering with Object directly is much farther under the hood than you want to be.
Instead, I'd make a new, simple object type like so:
const Dictionary = function() {}
Dictionary.prototype.add = function(key) {
if (this.hasOwnProperty(key)) {
this[key] += 1;
} else {
this[key] = 1;
}
}
const dict = new Dictionary();
dict.add("apples");
console.log(dict.apples) // 1
dict.add("apples");
dict.add("bananas");
console.log(dict.apples, dict.bananas) // 2, 1
It's not quite what you wanted since you have to call add each time, but I'd take the three-character tradeoff for the sake of simplicity and extensibility.
Codepen
I've been under the impression that only Array objects have a .length property. But, then again, I've also seen mentions of objects that are "array-like". I've not looked into this, and now it seems like my ignorance of this topic in JS may be biting me in the ass. Case in point:
I've got the following code:
var View = function(options) {
// code
};
_.extend(View, Backbone.Events, {
make_children: function(parent) {
// code
}
});
Later on, I use this View Function with Underscore's _.each, which decides this function object is an array, because it has a .length property:
// Code from Underscore.js's `_.each`:
} else if (obj.length === +obj.length) { // This is true
for (var i = 0, l = obj.length; i < l; i++) { // **So, execution goes here**
if (iterator.call(context, obj[i], i, obj) === breaker) return
}
} else {
for (var key in obj) {
if (_.has(obj, key)) { // **Execution does __not__ go here**
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
This results in code that doesn't work, because obj[i] where i is an integer index, is not actually defined on my obj View. To be precise, in the above code, obj[0] is undefined while obj.length === +obj.length is true and obj.length is 1. What's going on here?
Addendum
Underscore's chief maintainer says the following on https://github.com/documentcloud/underscore/pull/510:
Simply making each reject function objects doesn't really help. We've
made a conscious decision to use a numerical length property to detect
array-like objects.
Instead, don't pass function objects to each.
Addendum 2
Realized that since I couldn't pass a function object to _.each, I could just "cast it" to a regular object like so:
var regular_obj = _.extend({}, View);
The issue here is that underscore.js, much like jquery, both use the .length property as a flag in their each functions. When the length property is present, the function assumes that the argument passed can be iterated through with a normal for loop. The reason behind this logic is there is an expectation that when the length property is defined then it is possible to iterate through the argument in order which is why the for loop is used.
The result of misusing length is essentially a name collision where there is an unintended result. I would suggest changing length to another synonym such as size or capacity or totalViews, etc.
Edit
If there are no other alternatives for you to use, and you must have length in there while still retaining _.each's functionality, then you can slightly hack it. This plug works with the minified version of underscore version 1.4.3
var s = Array.prototype.ForEach;
var r = {};
var myEach = function (n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length&&typeof(n[0])!="undefined"){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(_.has(n,a)&&t.call(e,n[a],a,n)===r)return};
_.each=myEach;
Here is a demo: http://jsfiddle.net/Xa5qq/
Basically what it does is use forEach when the length property exists but typeof(yourObject[0]) == "undefined".
Which Objects in JavaScript have a .length property?
By oh-so-tautological definition, any object which has a length property.
This happens to include functions.
length is a property of a function object, and indicates how many arguments the function expects, i.e. the number of formal parameters.
This is also array-like, because it has a length:
var foo = {
bar: true,
baz: 'quux',
length: 42
}
I'm making a game, and I've come across a problem... When I try to save, JSON fails and reports that circular reference is being made somewhere. I don't think it actually is, I can't see it, so is there an algorithm or anything which could tell me where it is exactly (between which objects and stuff)? Also, is there a JSON alternative that can save circular reference? I'm running a node.js server, I saw this, but I can't get it to work (it's not made as a module i can require() in my code).
If you want to serialize a circular reference so you can save it, you need to make the reference "virtual" in that it can't be serialized as a circular reference, since that would cause serialization to serialize the same circle of objects forever (or at least until the runtime has run out of memory).
So instead of storing the circular reference itself, you just store a pointer to the object. The pointer will just be something like ref : '#path.to.object' that can be resolved when you deserialize so you point the reference back to the actual object. You just need to break the reference on serialization to be able to serialize it.
Discovering a circular reference in JavaScript can be done by recursively iterating through all objects (with for (x in y)), store x in an array and compare each x with the identity operator (a.k.a. strict comparison operator) === for each z in the temporary array. Whenever x === z equals true, replace the reference to x with a placeholder that will be serialized to the above mentioned ref.
An alternative to keeping an array over "visited" objects is to "taint" the objects you iterate through by setting a property on them, like in this very naïve example:
for (x in y) {
if (x.visited) {
continue;
}
x.visited = true;
}
There is no good way to detect circularity in objects but it is possible though by walking the object tree and checking references. I baked up a node-walking function that tries to detect if a node has been already used as its parent
function isCircularObject(node, parents){
parents = parents || [];
if(!node || typeof node != "object"){
return false;
}
var keys = Object.keys(node), i, value;
parents.push(node); // add self to current path
for(i = keys.length-1; i>=0; i--){
value = node[keys[i]];
if(value && typeof value == "object"){
if(parents.indexOf(value)>=0){
// circularity detected!
return true;
}
// check child nodes
if(arguments.callee(value, parents)){
return true;
}
}
}
parents.pop(node);
return false;
}
And the usage would be isCircularObject(obj_value) where the function returns true if circularity exists and false if not.
// setup test object
var testObj = {
property_a:1,
property_b: {
porperty_c: 2
},
property_d: {
property_e: {
property_f: 3
}
}
}
console.log(isCircularObject(testObj)); // false
// add reference to another node in the same object
testObj.property_d.property_e.property_g = testObj.property_b;
console.log(isCircularObject(testObj)); // false
// add circular node
testObj.property_b.property_c = testObj.property_b;
console.log(isCircularObject(testObj)); // true
The key point being that an object value is equal with an other value only if it is the same object reference and not when it's another object (even if completely similar).
This is a small extension to Andris' answer that tells you where the first circular element is so you can deal with it accordingly.
function findCircularObject(node, parents, tree){
parents = parents || [];
tree = tree || [];
if (!node || typeof node != "object")
return false;
var keys = Object.keys(node), i, value;
parents.push(node); // add self to current path
for (i = keys.length - 1; i >= 0; i--){
value = node[keys[i]];
if (value && typeof value == "object") {
tree.push(keys[i]);
if (parents.indexOf(value) >= 0)
return true;
// check child nodes
if (arguments.callee(value, parents, tree))
return tree.join('.');
tree.pop();
}
}
parents.pop();
return false;
}
If you don't want a string, the tree array is unnecessary. Just change the original function to
return value;
for the circular object itself or
return parents.pop();
for its parent.
I was thinking about what you're trying to accomplish based off the initial code from your other question. Why not do something like this.
Player = function()
{
this.UnitTypeXpower = 2
this.UnitTypeYpower = 7
}
UnitTypeXAdd = function(owner)
{
owner.UnitTypeXpower++;
}
That way you don't have to use a circular reference and it accomplishes the same thing.
Here is the code that I am using to detect circular references, it uses the technique that was suggested in the accepted answer by asbjornu, whereby each value is walked through and its reference is maintained in an array so that the next value can be compared with those previously walked.
function isCircular(obj, arr) {
"use strict";
var type = typeof obj,
propName,
//keys,
thisVal,
//iterKeys,
iterArr,
lastArr;
if (type !== "object" && type !== "function") {
return false;
}
if (Object.prototype.toString.call(arr) !== '[object Array]') {
//if (!Array.isArray(arr)) {
type = typeof arr; // jslint sake
if (!(type === "undefined" || arr === null)) {
throw new TypeError("Expected attribute to be an array");
}
arr = [];
}
arr.push(obj);
lastArr = arr.length - 1;
for (propName in obj) {
//keys = Object.keys(obj);
//propName = keys[iterKeys];
//for (iterKeys = keys.length - 1; iterKeys >= 0; iterKeys -= 1) {
thisVal = obj[propName];
//thisVal = obj[keys[iterKeys]];
type = typeof thisVal;
if (type === "object" || type === "function") {
for (iterArr = lastArr; iterArr >= 0; iterArr -= 1) {
if (thisVal === arr[iterArr]) {
return true;
}
}
// alternative to the above for loop
/*
if (arr.indexOf(obj[propName]) >= 0) {
return true;
}
*/
if (isCircular(thisVal, arr)) {
return true;
}
}
}
arr.pop();
return false;
}
This code is available on jsfiddle, where you can test it for yourself.
I have also run some performance tests on jsperf.
Array.indexOf was only introduced as of Javascript 1.6, see MDN page
Array.isArray was only introduced as of Javascript 1.8.5, see MDN page
Object.keys was only introduced as of Javascript 1.8.5, see MDN page
It is also worth noting that arguments.callee is deprecated and forbidden in strict mode in preference to using named functions
This thread already contains some good answers. If you are looking for a way to detect circular references or compare two values while dealing with circular references, you should check out this library:
https://www.npmjs.com/package/#enio.ai/data-ferret
It has the following methods:
hasCircularReference(someValue) // A predicate that returns true when it detects circular reference.
isIdentical(valueA, valueB) // By calling setConfig(options) opt-in circular reference support, this function does an equality check that does not fall into an infinite recursion trap.
Under the hood, it uses a similar algorithm to what #Asbjørn Ulsberg describes, but cleans after itself by removing all flags inserted.
However, the primary difference between the algorithms discussed here and the suggestion is that it can deal with any number of native/custom, iterable/non-iterable classes, meaning it supports circular detection and value comparison beyond the JSON specification and JavaScript's Object and Array.
All that's required for it to handle other classes is to call:
registerClassTypes()
registerIterableClass()
This library comes with 100% code coverage, so you can check out how to use the API by reading the .spec files if you visit the GitHub page.
Disclaimer: I wrote this library, but I do think there is a legitimate reason to mention it as it gives additional features that you might need when dealing with circular dependencies.
If I want to enumerate the properties of an object and want to ignore prototypes, I would use:
var instance = { ... };
for (var prop in instance) {
if (instance.hasOwnProperty(prop)) {
...
}
}
What if instance only has one property, and I want to get that property name? Is there an easier way than doing this:
var instance = { id: "foobar" };
var singleMember = (function() {
for (var prop in instance) {
if (instance.hasOwnProperty(prop)) {
return prop;
}
}
})();
Maybe Object.keys can work for you. If its length returns 1, you can use yourObject[Object.keys[0]] to get the only property of the object. The MDN-link also shows a custom function for use in environments without the keys method1. Code like this:
var obj = {foo:'bar'},
kyz = Object.keys(obj);
if (kyz.length === 1){
alert(obj[kyz[0]]); //=> 'bar'
} else {
/* loop through obj */
}
1 Some older browsers don't support Object.keys. The MDN link supplies code to to make it work in these browsers too. See header Compatibility in the aforementioned MDN page
Shortest form:
instance[Object.keys(instance)[0]];
ES6+ function:
let first = v => v[Object.keys(v)[0]];
Use the function:
first({a:'first', b:'second'}) // return 'first'
var foo = {bar: 1};
console.log(Object.keys(foo).toString());
which will print the string
"bar"
Though my answer is downvoted, it's still worth to know that there is no such thing as order of keys in javascript object. Therefore, in theory, any code build on iterating values can be inconsistent. One approach could be creating an object and to define setter which actually provides counting, ordering and so on, and provide some methods to access this fields. This could be done in modern browsers.
So, to answer you question, in general you approach is still most closs-browser. You can iterate using lodash or any other modern framework wich will hide "hasOwnProperty" complexity from you. As of August'15 Object.keys can be accepted as cross-browser and universal. After all IE8 happened years ago. Still there are some cases when you just don't wont store all set of keys in array. But I'd go with Object.keys - it's more flexible compared to iteration.
Unfortunately, there is no, "list properties" function built in, and there certainly isn't a "getFirstProperty" (especially since there is no guarantee that any property will consistently be "first").
I think you're better off writing a function like this one:
/**
* A means to get all of the keys of a JSON-style object.
* #param obj The object to iterate
* #param count maximum length of returned list (defaults to Infinity).
*/
function getProperties( obj, count )
{
if( isNaN( count ) ) count = Infinity
var keys = []
for( var it in obj )
{
if( keys.length > count ) break;
keys.push( it );
}
return keys;
}
Then, you could access the name though:
instance = {"foo":"bar"}
// String() on an array of < 2 length returns the first value as a string
// or "" if there are no values.
var prop = String(getProperties(instance, 1));
This is an old post, but I ended up writing the following helper function based on Object.keys().
It returns the key and value of the first property.
getFirstPropertyKeyAndValue(sourceObject) {
var result = null;
var ownProperties = Object.keys(sourceObject);
if (ownProperties.length > 0) {
if (ownProperties.length > 1) {
console.warn('Getting first property of an object containing more than 1 own property may result in unexpected results. Ordering is not ensured.', sourceObject);
}
var firstPropertyName = ownProperties[0];
result = {key: firstPropertyName, value: sourceObject[firstPropertyName]};
}
return result;
}
Answers in here all good, and with the caveat that the order may be unreliable (although in practice it seems the order the properties are set tends to stay that way), this quick and dirty method also works:
var obj = {foo: 1, bar: 2};
for(var key in obj) {
//you could use key here if you like
break;
}
//key now contains your first key
or a shorter version should also do it:
for(var key in obj) break;
//key now contains your first key
What's the fastest way to count the number of keys/properties of an object? Is it possible to do this without iterating over the object? I.e., without doing:
var count = 0;
for (k in myobj) if (myobj.hasOwnProperty(k)) ++count;
(Firefox did provide a magic __count__ property, but this was removed somewhere around version 4.)
To do this in any ES5-compatible environment, such as Node.js, Chrome, Internet Explorer 9+, Firefox 4+, or Safari 5+:
Object.keys(obj).length
Browser compatibility
Object.keys documentation (includes a method you can add to non-ES5 browsers)
You could use this code:
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],
k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
Then you can use this in older browsers as well:
var len = Object.keys(obj).length;
If you are using Underscore.js you can use _.size (thanks douwe):
_.size(obj)
Alternatively you can also use _.keys which might be clearer for some:
_.keys(obj).length
I highly recommend Underscore.js. It's a tight library for doing lots of basic things. Whenever possible, they match ECMAScript 5 and defer to the native implementation.
Otherwise I support Avi Flax' answer. I edited it to add a link to the MDC documentation which includes the keys() method you can add to non-ECMAScript 5 browsers.
The standard Object implementation (ES5.1 Object Internal Properties and Methods) does not require an Object to track its number of keys/properties, so there should be no standard way to determine the size of an Object without explicitly or implicitly iterating over its keys.
So here are the most commonly used alternatives:
1. ECMAScript's Object.keys()
Object.keys(obj).length; Works by internally iterating over the keys to compute a temporary array and returns its length.
Pros - Readable and clean syntax. No library or custom code required except a shim if native support is unavailable
Cons - Memory overhead due to the creation of the array.
2. Library-based solutions
Many library-based examples elsewhere in this topic are useful idioms in the context of their library. From a performance viewpoint, however, there is nothing to gain compared to a perfect no-library code since all those library methods actually encapsulate either a for-loop or ES5 Object.keys (native or shimmed).
3. Optimizing a for-loop
The slowest part of such a for-loop is generally the .hasOwnProperty() call, because of the function call overhead. So when I just want the number of entries of a JSON object, I just skip the .hasOwnProperty() call if I know that no code did nor will extend Object.prototype.
Otherwise, your code could be very slightly optimized by making k local (var k) and by using prefix-increment operator (++count) instead of postfix.
var count = 0;
for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count;
Another idea relies on caching the hasOwnProperty method:
var hasOwn = Object.prototype.hasOwnProperty;
var count = 0;
for (var k in myobj) if (hasOwn.call(myobj, k)) ++count;
Whether this is faster or not on a given environment is a question of benchmarking. Very limited performance gain can be expected anyway.
Here are some performance tests for three methods;
https://jsperf.com/get-the-number-of-keys-in-an-object
Object.keys().length
20,735 operations per second
It is very simple and compatible and runs fast but expensive, because it creates a new array of keys, which then gets thrown away.
return Object.keys(objectToRead).length;
Loop through the keys
15,734 operations per second
let size=0;
for(let k in objectToRead) {
size++
}
return size;
It is slightly slower, but nowhere near the memory usage, so it is probably better if you're interested in optimising for mobile or other small machines.
Using Map instead of Object
953,839,338 operations per second
return mapToRead.size;
Basically, Map tracks its own size, so we're just returning a number field. It is far, far faster than any other method. If you have control of the object, convert them to maps instead.
If you are actually running into a performance problem I would suggest wrapping the calls that add/remove properties to/from the object with a function that also increments/decrements an appropriately named (size?) property.
You only need to calculate the initial number of properties once and move on from there. If there isn't an actual performance problem, don't bother. Just wrap that bit of code in a function getNumberOfProperties(object) and be done with it.
As answered in a previous answer: Object.keys(obj).length
But: as we have now a real Map class in ES6, I would suggest to use it instead of using the properties of an object.
const map = new Map();
map.set("key", "value");
map.size; // THE fastest way
this works for both, Arrays and Objects
//count objects/arrays
function count(obj){
return Object.keys(obj).length
}
count objects/arrays with a Loop
function count(obj){
var x=0;
for(k in obj){
x++;
}
return x;
}
count objects/arrays or also the length of a String
function count(obj){
if (typeof (obj) === 'string' || obj instanceof String)
{
return obj.toString().length;
}
return Object.keys(obj).length
}
As stated by Avi Flax,
Object.keys(obj).length
will do the trick for all enumerable properties on your object, but to also include the non-enumerable properties, you can instead use the Object.getOwnPropertyNames. Here's the difference:
var myObject = new Object();
Object.defineProperty(myObject, "nonEnumerableProp", {
enumerable: false
});
Object.defineProperty(myObject, "enumerableProp", {
enumerable: true
});
console.log(Object.getOwnPropertyNames(myObject).length); //outputs 2
console.log(Object.keys(myObject).length); //outputs 1
console.log(myObject.hasOwnProperty("nonEnumerableProp")); //outputs true
console.log(myObject.hasOwnProperty("enumerableProp")); //outputs true
console.log("nonEnumerableProp" in myObject); //outputs true
console.log("enumerableProp" in myObject); //outputs true
As stated here, this has the same browser support as Object.keys.
However, in most cases, you might not want to include the nonenumerables in these type of operations, but it's always good to know the difference ;)
To iterate on Avi Flax' answer, Object.keys(obj).length is correct for an object that doesn’t have functions tied to it.
Example:
obj = {"lol": "what", owo: "pfft"};
Object.keys(obj).length; // should be 2
versus
arr = [];
obj = {"lol": "what", owo: "pfft"};
obj.omg = function(){
_.each(obj, function(a){
arr.push(a);
});
};
Object.keys(obj).length; // should be 3 because it looks like this
/* obj === {"lol": "what", owo: "pfft", omg: function(){_.each(obj, function(a){arr.push(a);});}} */
Steps to avoid this:
do not put functions in an object that you want to count the number of keys in
use a separate object or make a new object specifically for functions (if you want to count how many functions there are in the file using Object.keys(obj).length)
Also, yes, I used the _ or Underscore.js module from Node.js in my example.
Documentation can be found here as well as its source on GitHub and various other information.
And finally a lodash implementation https://lodash.com/docs#size
_.size(obj)
I'm not aware of any way to do this. However, to keep the iterations to a minimum, you could try checking for the existence of __count__ and if it doesn't exist (i.e., not Firefox) then you could iterate over the object and define it for later use, e.g.:
if (myobj.__count__ === undefined) {
myobj.__count__ = ...
}
This way, any browser supporting __count__ would use that, and iterations would only be carried out for those which don't. If the count changes and you can't do this, you could always make it a function:
if (myobj.__count__ === undefined) {
myobj.__count__ = function() { return ... }
myobj.__count__.toString = function() { return this(); }
}
This way, any time you reference myobj.__count__ the function will fire and recalculate.
From Object.defineProperty():
Object.defineProperty(obj, prop, descriptor)
You can either add it to all your objects:
Object.defineProperty(Object.prototype, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Or a single object:
var myObj = {};
Object.defineProperty(myObj, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Example:
var myObj = {};
myObj.name = "John Doe";
myObj.email = "leaked#example.com";
myObj.length; // Output: 2
Added that way, it won't be displayed in for..in loops:
for(var i in myObj) {
console.log(i + ": " + myObj[i]);
}
Output:
name: John Doe
email: leaked#example.com
Note: it does not work in browsers before Internet Explorer 9.
For those who have Underscore.js included in their project you can do:
_({a:'', b:''}).size() // => 2
or functional style:
_.size({a:'', b:''}) // => 2
How I've solved this problem is to build my own implementation of a basic list which keeps a record of how many items are stored in the object. It’s very simple. Something like this:
function BasicList()
{
var items = {};
this.count = 0;
this.add = function(index, item)
{
items[index] = item;
this.count++;
}
this.remove = function (index)
{
delete items[index];
this.count--;
}
this.get = function(index)
{
if (undefined === index)
return items;
else
return items[index];
}
}
For those that have Ext JS 4 in their project, you can do:
Ext.Object.getSize(myobj);
The advantage of this is that it'll work on all Ext JS compatible browsers (Internet Explorer 6 - Internet Explorer 8 included). However, I believe the running time is no better than O(n) though, as with other suggested solutions.
You can use:
Object.keys(objectName).length;
and
Object.values(objectName).length;
The OP didn't specify if the object is a nodeList. If it is, then you can just use the length method on it directly. Example:
buttons = document.querySelectorAll('[id=button)) {
console.log('Found ' + buttons.length + ' on the screen');
If jQuery in previous answers does not work, then try
$(Object.Item).length
I try to make it available to all objects like this:
Object.defineProperty(Object.prototype,
"length",
{
get() {
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
return Object.keys(this).length;
},});
console.log({"Name":"Joe", "Age":26}.length) // Returns 2