I have a simple object of objects:
window.abilities = {
migrate:{
name:"Migrate",
description:"Move your tribe to another area; generate all new resources. Takes one time unit.",
image:"migrate.png",
action:"Migrate",
unlocked:true
},
eradicate:{
name:"Eradicate species",
description:"Remove a troublesome plant or animal",
image:"migrate.png",
action:"Eradicate",
unlocked:false
}
}
I am using a for ... in ... loop to iterate over this object and generate UI elements:
for(ability in window.abilities){
if(ability.unlocked){
$("#abilities").append(genAbilityCard(ability.name,ability.image,ability.description,ability.action));
}
}
However, each ability variable is empty - it only has the key and not the properties (name, description, and so forth). These properties seem to be unenumerable - even though properties created this way should be enumerable by default!
How can I make these properties enumerable implicitly, without using Object.defineProperty or something unwieldy like that?
If you're able to use ES6 and the for... of loop, Alberto's answer is your best bet, and the cleanest option.
If you're limited to ES5, you can use for ... in as well. As you've found, for ... in only enumerates the keys (the property names), not the property values, but it's easy to get the property value once you know the property name:
var abilityName, ability;
for(abilityName in window.abilities){
ability = window.abilities[abilityName];
if(ability.unlocked){
$("#abilities").append(genAbilityCard(ability.name,ability.image,ability.description,ability.action));
}
}
The fact you're getting the property names means you ARE successfully enumerating the properties. Javascript is just a bit weird in how for ... in works.
Try iterating by using for..of and Object.values (ES6 only):
for (const ability of Object.values(window.abilities)){
if (ability.unlocked){
$("#abilities").append(genAbilityCard(ability.name,ability.image,ability.description,ability.action));
}
}
Related
It used to be that, when I used console.log(obj) to display an object's properties in Javascript, the properties would display alphabetically. Recently, this seems to have changed. I assume that the new order is closer to how the properties are actually stored in the code, (though I was under the impression that object properties didn't have a "real" fixed order to begin with).
However, for large, complex classes, the alphabetical display was very useful for finding the property that I was looking for. Is there an option for making the properties display alphabetically, as before?
(I would also like to know what exactly the current order of properties "represents" and how it is determined in the first place.)
though I was under the impression that object properties didn't have a "real" fixed order to begin with
They do, as of ES2015 (and even operations that previously were exempt are being brought in line now), but you probably want to continue to act as though they don't. For own properties, it's the order they were added to the object (except for integer index properties which are listed in numeric order before other properties); details in the answers to this question. (There is no defined order for inherited properties, although engines are fairly consistent about what they do with them: They list them after own properties, following the same order rules. But again, that's not specified.)
Recently, this seems to have changed.
Looking at it experimentally, it looks like it's following property order now.
You could give yourself a utility function that creates a new object with the properties added to it in alphabetical order:
function logAlpha(obj) {
const x = {};
for (const key of Object.keys(obj).sort()) {
x[key] = obj[key];
}
console.log(x);
}
Of course, unlike console.log(obj), that will be a snapshot, not a live link to the original object.
You can do a lot to tweak that (including the prototype properties, including the prototype itself, including sorted versions of the full prototype chain, etc.), but that's the gist.
Lets say I want to check whether a property exists within an Object or not. I was looking at two methods:
if(object.hasOwnProperty(key)) { /* do this */ }
OR
if(object[key]) { /* do this */ }
Assuming that we'll never have object = { key: false };
What are the tradeoffs of using one over the other ?
object[key] checks the entire object chain, including the prototype chain.
object.hasOwnProperty(key) checks to see if the key property is assigned directly to the object and does not look in the prototype chain.
So, which one to use depends upon which result you want.
See a simple demonstration using the hasOwnProperty property: http://jsfiddle.net/jfriend00/6zhv87rk/ which is on the prototype for an object.
You would typically use object.hasOwnProperty(key) when your key can be any arbitrary string and you want to make absolutely sure that it will never conflict with a built-in property on the Object prototype. In fact, if you really want to protect yourself, you even use this Object.prototype.hasOwnProperty.call(object, key) so even the hasOwnProperty property could be used with your object. Cases like this would be when you're using a Javascript object as a hash lookup or dictionary-type object with arbitrary key values.
You would typically use object[key] when you are not concerned about such a conflict because you know what types of keys will be on your object and you know they won't conflict because this version is just shorter and a bit easier to read.
I'm currently making a chat AI that uses markov chains. One of the backend objects is an object that maps a string to any chain that it is a part of, such that:
var words = {}
words['test'] = [ ['this','is','a','test'], ['this','too','tests','things'] ]
Now, I have a problem. 'constructor' is a perfectly valid word, but it appears as though you can't map it as I have described above, as 'constructor' is a property of every object in JS (along with other things, of course). So to my question: is there a way to construct this map?
constructor isn't a property of every object. Though by default every object created from literal syntax will have constructor in its prototype chain. This is because those objects inherit from Object.prototype, which does have that property.
One solution is to use objects that have no prototype chain.
var words = Object.create(null);
words['test'] = [ ['this','is','a','test'], ['this','too','tests','things'] ];
Now there will be no inherited properties to confuse things. This won't work in IE8 and lower though.
Another solution is to use .hasOwnProperty() to see if the constructor property is on the object itself or inherited. If inherited, then it's not the one you want.
if (words.hasOwnProperty("constructor"))
console.log(words.constructor);
If the condition passes, then we know we're not using the inherited property, but rather the object's own property.
A clientside javascript library I've developed uses objects as hashes in some areas. It loops through objects parsed from Json data with a for...in loop using the property name as a key. eg... (pseudo code)
var conversations = {'sha1-string':{name:'foo',messages:[]}}
for(var id in conversations){
console.log(id);
console.log(conversations[id].name);
}
Unfortunately MooTools (and Prototype, etc) add methods to the global namespaces, so my for...in loops now iterate through MooTools' additions (eg. limit, round, times, each), causing errors when it applies logic to them as if it were the data expected.
As it's a library, I have to expect that it will be used with MooTools, Prototype, etc. Is there an easy way around this problem? My current solution is just to pass the object to a method which strips out the MooTools specific entries and returns the clean object, but this means also checking what Prototype and all similar libraries out there add, and seems to be a backwards way of doing things.
My other solution is to stop relying on the property name as a key, and perform validation in the loops to ensure I'm looking at the data I want to. Before I do that rewriting though, I'm wondering if anyone has a better/existing solution?
Thanks :)
If your clientside objects are not inherited from other custom objects, you see if you could use the javascript's Object.hasOwnProperty method to find out if a certain property exists in the object itself and not up in the inheritance chain via the prototype object.
For browsers that don't support this method, you can write a wrapper around to check:
var hasOwnProperty = function(object, property) {
if(object.hasOwnProperty) {
return object.hasOwnProperty(property);
}
var prop = object[property];
return typeof(prop) !== "undefined" && prop !==
object.constructor.prototype[property];
}
How to use it:
for(var key in someObj) {
if(hasOwnProperty(someObj, key)) {
// we are good to go!
}
}
If I got it correctly, you are looking for the hasOwnProperty() method.
As "limit", "round", "times" and "each" are all additional methodes of the Array prototyp you're main failure is to use "for in" to iterate over arrays. You should check the type of the object before and use "for in" only for objects and "for" for arrays.
Btw. "for in" is the slowest method to iterate over arrays.
Using both MooTools and Prototype isn't a very good idea as they modify the base prototypes and can't co-exist peacefully. That said, none of them modify Object.prototype (not anymore) so you should not see any other properties besides your own.
Did you add a plugin which is perhaps adding these properties there? Could you give a more complete example that shows where these properties are getting listed and also the versions of these libraries that you are using?
hasOwnProperty checks will solve the prototype problem, but this shouldn't be needed in the first place, so maybe it's better to find out exactly which script is messing up.
MooTools has created a Hash type just because they didn't want to modify Object.prototype. Here's a quote from the docs:
Hash
A custom Object ({}) implementation which does not account for prototypes when setting, getting, or iterating. Useful because in JavaScript, we cannot use Object.prototype. Instead, we can use Hash.prototype!
var foo = { "bar": {"blah": 9 } };
Is there a way to get the ["blah"] value of the only member of foo if I don't know the key is "bar"?
Can I somehow reference the first member of an object without knowing its key?
I'm looking for the equivalent of
foo[0]["blah"] if foo were a normal array.
In my case, I can't practically iterate on foo to get the key.
Is that clear?
As far as I know, with a Javascript Object (in the literal sense, the object of type `Object) the only way to do this is:
for(var i in foo) {
var value = foo[i].blah;
break;
}
Now value will contain the value of the first enumerable property of the bar object in the foo object. You could, of course, abstract this into a function. I was going to write an example, but CMS has a fantastic one in his answer here.
Edit: Agree with #kangax, it's much more safe to make a normal function, without polluting the native Object.prototype, which can lead to unexpected behaviors:
function firstMember (obj) {
for(var i in obj)
if (obj.hasOwnProperty(i)){ // exclude properties from the prototype
return obj[i];
}
}
firstMember(foo)['blah']; // 9
Objects in Javascript are unordered collection of name/value pairs, so there's really no such thing as accessing first or last property of an object. The only way to find certain key's value is to iterate over an object (with for-in). You can stop on the first iteration, but an order is not specified, so two different implementations can return two different keys on a first iteration.
I'm afraid what you're attempting to do is just not possible. Objects are essentially hash maps, so the order in which properties are stored should appear at a random location in the object's memory (And the better the hash function, the more random the locations).
You can see this for yourself if you step through a loop that iterates over all the properties of an object. There is no set order in which to iterate through because of the random nature of the structure.
Perhaps you could store the values in both an object and an array?
Just have a look here:
http://dean.edwards.name/weblog/2006/07/enum/
how they iterate through the object without knowing its structure.