views:

11097

answers:

6

Say I create an object thus:

var myJSONObject =
        {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};

What is the best way to retrieve a list of the property names? i.e. I would like to end up with some variable 'keys' such that:

keys == ["ircEvent", "method", "regex"]

Thanks.

+34  A: 

You can use the following and pass it an object.

var getKeys = function(obj){
   var keys = [];
   for(var key in obj){
      keys.push(key);
   }
   return keys;
}

Alternatively replace var getKeys with Object.prototype.keys to allow you to call .keys() on any object. Extending the prototype has some side effects and I wouldn't recommend doing it.

slashnick
would or wouldn't? ;)
Chris MacDonald
doh' I've updated it now
slashnick
I would update again to the effect 'you might be tempted to do this to object prototype ... but don't!'
AnthonyWJones
+17  A: 

As slashnick pointed out, you can use the "for in" construct to iterate over an object for its attribute names. However you'll be iterating over all attribute names in the object's prototype chain. If you want to iterate only over the object's own attributes, you can make use of the Object#hasOwnProperty() method. Thus having the following.

for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
        /* useful code here */
    }
}
Pablo Cabrera
should be hasOwnPoperty
Brent Plump
hasOwnProperty**Thanks for pointing out ;)
Pablo Cabrera
I wish I had read this before slashnic's answer above. I just had to spend 15 minutes holding down the `esc` key because the object had about a million properties, most of them not used, and I had an alert on it.
Farseeker
Pablo Cabrera
A: 

IE does not support for(i in obj) for native properties. Here is a list of all the props I could find.

It seems stackoverflow does some stupid filtering.

The list is available at the bottom of this google group post:- https://groups.google.com/group/hackvertor/browse_thread/thread/a9ba81ca642a63e0

A: 

I'm a huge fan of the dump function.

http://ajaxian.com/archives/javascript-variable-dump-in-coldfusion alt text

Matt
+2  A: 

Note that Object.keys and other ECMAScript 5 methods are supported by Firefox 4, Chrome 6, Safari 5, IE 9 and above.

For example:

var o = {"foo": 1, "bar": 2}; 
alert(Object.keys(o));

ECMAScript 5 compatibility table: http://kangax.github.com/es5-compat-table/

Description of new methods: http://markcaudill.com/index.php/2009/04/javascript-new-features-ecma5/

Sam Dutton
+5  A: 

As Sam Dutton answered, a new method for this very purpose has been introduced in ECMAScript 5th Edition. Object.keys() will do what you want and is supported in Firefox 4, Chrome 6, Safari 5 and IE 9.

You can also very easily implement the method in browsers that don't support it. However, some of the implementations out there aren't fully compatible with Internet Explorer. I've detailed this on my blog and produced a more compatible solution:

(function () {
    var hasOwnProperty = Object.prototype.hasOwnProperty,
        hasDontEnumBug = true,
        DontEnums = [ 
            'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty',
            'isPrototypeOf', 'propertyIsEnumerable', 'constructor'
        ],
        DontEnumsLength = DontEnums.length;

    for (var k in {toString:null})
        hasDontEnumBug = false;

    Object.keys = Object.keys || function (o) {
        if (typeof o != "object" && typeof o != "function" || o === null)
            throw new TypeError("Object.keys called on a non-object");

        var result = [];
        for (var name in o) {
            if (hasOwnProperty.call(o, name))
                result.push(name);
        }

        if (hasDontEnumBug) {
            for (var i = 0; i < DontEnumsLength; i++) {
                if (hasOwnProperty.call(o, DontEnums[i]))
                    result.push(DontEnums[i]);
            }   
        }

        return result;
    };
})();

Note that the currently accepted answer doesn't include a check for hasOwnProperty() and will return properties that are inherited through the prototype chain. It also doesn't account for the famous DontEnum bug in Internet Explorer where non-enumerable properties on the prototype chain cause locally declared properties with the same name to inherit their DontEnum attribute.

Implementing Object.keys() will give you a more robust solution.

EDIT: following a recent discussion with kangax, a well-known contributor to Prototype, I implemented the workaround for the DontEnum bug based on code for his Object.forIn() function found here.

Andy E
Great answer, I think the accepted answer remains the most performant accurate solution, assuming that it is always a JSON dict. This is certainly the one to use elsewhere.
David Caunt
@David Caunt: Thanks :-) Unfortunately, the accepted answer would still fall foul of the DontEnum bug and you never know what JSON object might have a string like "valueOf" or "constructor" as one of its keys. It will also iterate over extensions to `Object.prototype`. It's often the case, though, that shorter code looks significantly more appealing than larger, more robust code, but the point of this answer is to use ECMAScript 5th's `Object.keys()`, which can be implemented in browsers that don't support it using this code. The native version would be even more performant than this.
Andy E
Good point - you're right about the bug. I hope any good developer would test for the native methods ahead of using their own implementations.
David Caunt
Very nice, Andy :) I would just like to remind — no one seems to mention in this thread — that ES5 `Object.keys` only returns an array of strings corresponding to **enumerable** properties of an object. This might not be crucial when working with native (user-defined) objects, but should be very much visible with host objects (although unspecified host objects behavior is a separate — painful — story). To enumerate over ALL (including non-enumerable) properties, ES5 provides `Object.getOwnPropertyNames` (see its support in my compat. table — http://kangax.github.com/es5-compat-table/)
kangax
I've integrated this solution into es5-shim http://github.com/kriskowal/es5-shim/blob/master/es5-shim.js#L390
Kris Kowal
@Kris: that's great, thanks for crediting :-) @kangax: yes, that's definitely worth mentioning and just reminded me of a piece of code I need to update, thanks :-)
Andy E
Of course this still only works for `Object`. If you did something silly like putting a shadowing `getFullYear` property on a `Date` instance this would still fail to make that enumerable. To get around that you'd have to have a much longer list of all IE<9 native properties, and check every one with both `hasOwnProperty` and `propertyIsEnumerable`. Not much fun.
bobince
@bobince: not much fun, indeed! Hopefully, this will tide us over until IE<9 is a distant memory :-)
Andy E