dynamic keys for object literals in Javascript

JavascriptObject Literal

Javascript Problem Overview


Ok so I'm working away on a project in Nodes, and I've come across a small problem with the keys in object literals, I have the following set-up:

var required = {
    directories : {
        this.applicationPath                    : "Application " + this.application + " does not exists",
        this.applicationPath + "/configs"       : "Application config folder does not exists",
        this.applicationPath + "/controllers"   : "Application controllers folder does not exists",
        this.applicationPath + "/public"        : "Application public folder does not exists",
        this.applicationPath + "/views"         : "Application views folder does not exists"
    },
    files : {
        this.applicationPath + "/init.js"               : "Application init.js file does not exists",
        this.applicationPath + "/controllers/index.js"  : "Application index.js controller file does not exists",
        this.applicationPath + "/configs/application.js": "Application configs/application.js file does not exists",
        this.applicationPath + "/configs/server.js"     : "Application configs/server.js file does not exists"
    }
}

Ok so many of you will look at this and think it look's OK, but the compiler keeps telling me that I am missing a : (colon), which im not, it seems like the + or and the . are both effecting the compiler.

Now i believe (not sure), that object literals are created at compile time, and not run-time, meaning that dynamic variables such as this.applicationPath and concatenation are not going to be available :( :(

What's the best way to overcome an obstacle like this without having to rewrite large chunks of code.

Javascript Solutions


Solution 1 - Javascript

Computed property names are supported in ECMAScript2015:

var name = 'key';
var value = 'value';
var o = {
  [name]: value
};
alert("o as json : " + JSON.stringify(o));

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer

Solution 2 - Javascript

Prior to ECMAScript 2015 (ed 6), an object literal (ECMAScript calls it an "object initializer") key must be one of:

  1. IdentifierName
  2. StringLiteral
  3. NumericLiteral

So you couldn't use an expression as the key in an initialiser. This was changed as of ECMAScript 2015 (see below). You could use an expression with square bracket notation to access a property, so to set the properties with an expression you had to do:

var required = { directories : {}};
required.directories[this.applicationPath] = "Application " + this.application + " does not exists";
required.directories[this.applicationPath + "/configs"] = "Application config folder does not exists";
...

and so on. Since this.applicationPath is reused a lot, better to store a reference to help with performance and cut down the amount of code:

var a = this.applicationPath;
var required = { directories : {}};
var rd = required.directories;
rd[a] = "Application " + this.application + " does not exists";
rd[a + "/configs"] = "Application config folder does not exists";
...
Edit

As of ECMAScript 2015 (ed 6), object initializers can have computed keys using:

[expression]: value

There is also shorthand syntax for property and method names.

See MDN: Object Initializer or ECMAScript Object Initializer.

Solution 3 - Javascript

You can set dynamic keys is with bracket notation:

required.directories[this.applicationPath + "/configs"] = "Application config folder does not exists";

(of course wherever you do this definition, this.applicationPath must exist)

But do you need this.applicationPath in the keys? How do you access theses values? Maybe you can just remove this.applicationPath from whatever value you use to access the properties.


But in case you need it:

You could use an array to initialize the keys if you want to avoid repeating a lot of code:

var dirs = ['configs', 'controllers', ...];
var files = ['init.js', 'controllers/index.js', ...];

var required = { directories: {}, files: {} };
required.directories[this.applicationPath] = "Application " + this.application + " does not exists";

for(var i = dirs.length; i--;) {
    required.directories[this.applicationPath + '/' + dirs[i]] = "Application " + dirs[i] + " folder does not exists";
}

for(var i = files.length; i--;) {
    // same here
}

Solution 4 - Javascript

Inspired by how babel coverts the new ES6 syntax ({[expression]: value}) to old Javascript, I learned that you can do it with a one liner:

var obj = (_obj = {}, _obj[expression] = value, _obj);

Example:

var dynamic_key = "hello";
var value = "world";
var obj = (_obj = {}, _obj[dynamic_key] = value, _obj);

console.log(obj);
// Object {hello: "world"}

(Tested on latest Chrome)

Solution 5 - Javascript

If you have a deep object structure (such as Grunt config), it's sometimes convenient to be able to return dynamically-generated object keys using the bracket notation outlined by Felix, but inline within the object structure. This can be achieved by using a function to dynamically return an object within the context of the deep object; in the case for the code in this question, something like this:

var required = {
    directories : function() {
        var o = {};
        o[this.applicationPath] = "Application " + this.application + " does not exists";
        o[this.applicationPath + "/configs"] = "Application config folder does not exists";
        o[this.applicationPath + "/controllers"] = "Application controllers folder does not exists";
        o[this.applicationPath + "/public"] = "Application public folder does not exists";
        o[this.applicationPath + "/views"] = "Application views folder does not exists";
        return o;
    }(),
    files : function() {
        var o = {};
        o[this.applicationPath + "/init.js"] = "Application init.js file does not exists";
        o[this.applicationPath + "/controllers/index.js"]  = "Application index.js controller file does not exists";
        o[this.applicationPath + "/configs/application.js"] ="Application configs/application.js file does not exists";
        o[this.applicationPath + "/configs/server.js"]     ="Application configs/server.js file does not exists";
        return o;
    }()
}

This fiddle validates this approach.

Solution 6 - Javascript

An old question, and the answers were correct at the time, but times change. In case someone digs it up in a google search, new javascript versions (ES6) allow using expressions as keys for object literals, if they are surrounded in square brackets: var obj={["a"+Math.PI]:42}

Solution 7 - Javascript

For object literals, Javascript/ECMAScript script specifies keys be either a valid IdentifierName, a string literal, or a number credit RobG (even hex) . Not an expression, which is what required.applicationPath + "/configs" is.

Solution 8 - Javascript

the problem is from using 'this' because it doesn't refer to anything smart*. create the static literal with the applicationPath in it.

var required={
"applicationPath":"someWhereOverTheRainboW"
};
Then use
required.directories={};
required.directories[required.applicationPath + "/configs"]="Application config folder does not exists";
....

to fill it dynamically

Edit; I rushed with my first idea, it didn't work. The above works now - sorry for that!

*** the keyword 'this' is very smart :)** but it often refers to the window object or the element, the event has been fired on or the called 'active' object. Thus, creating a lot of confusion ;)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRobertPittView Question on Stackoverflow
Solution 1 - JavascriptRich ApodacaView Answer on Stackoverflow
Solution 2 - JavascriptRobGView Answer on Stackoverflow
Solution 3 - JavascriptFelix KlingView Answer on Stackoverflow
Solution 4 - JavascriptDan BarzilayView Answer on Stackoverflow
Solution 5 - JavascriptDaveAldenView Answer on Stackoverflow
Solution 6 - Javascriptuser6451670View Answer on Stackoverflow
Solution 7 - JavascriptMooGooView Answer on Stackoverflow
Solution 8 - JavascriptjaprescottView Answer on Stackoverflow