Reputation: 9645
Suppose I have a json object in which I record the number of visitors to my site, grouped by browser / version.
let data = {
browsers: {
chrome: {
43 : 13,
44 : 11
},
firefox: {
27: 9
}
}
}
To increment a particular browser, I need to check if several keys exist, and if not, create them.
let uap = UAParser(request.headers['user-agent']);
if (typeof uap.browser !== 'undefined') {
if (typeof data.browsers === 'undefined')
data.browsers = {}
if (typeof data.browsers[uap.browser.name] === 'undefined')
data.browsers[uap.browser.name] = {}
if (typeof data.browsers[uap.browser.name][uap.browser.version] === 'undefined')
data.browsers[uap.browser.name][uap.browser.version] = 0
data.browsers[uap.browser.name][uap.browser.version] += 1;
}
The deeper my data structure the crazier things get.
It feels like there must be a neater way to do this in javascript. There's always a neater way. Can anyone enlighten me here?
Upvotes: 4
Views: 3318
Reputation: 987
Here's a very clean and generic solution using Proxy()
I have a second solution which is standard ECMAscript 5 if you don't need it so cleanly or need it less browser dependant.
var handler = {
get: function (target, property) {
if (property !== "toJSON" && target[property] === undefined) {
target[property] = new Proxy ({}, handler);
}
return target[property];
}
}
var jsonProxy = new Proxy ({}, handler);
jsonProxy.non.existing.property = 5;
jsonProxy.another.property.that.doesnt.exist = 2;
jsonProxy["you"]["can"]["also"]["use"]["strings"] = 20;
console.log (JSON.stringify (jsonProxy));
You can do the same thing with classes but with a more verbose syntax:
var DynamicJSON = function () {};
DynamicJSON.prototype.get = function (property) {
if (this[property] === undefined) {
this[property] = new DynamicJSON ();
}
return this[property];
};
DynamicJSON.prototype.set = function (property, value) {
this[property] = value;
};
var jsonClass = new DynamicJSON ();
jsonClass.get("non").get("existing").set("property", 5);
jsonClass.get("you").get("have").get("to").get("use").set("strings", 20);
console.log (JSON.stringify (jsonClass));
Upvotes: 1
Reputation: 72927
This shorter code should do the trick:
if (uap.browser) { // typeof is pretty much redundant for object properties.
const name = uap.browsers.name; // Variables for readability.
const version = uap.browser.version;
// Default value if the property does not exist.
const browsers = data.browsers = data.browsers || {};
const browser = browsers[name] = browsers[name] || {};
browser[version] = browser[version] || 0;
// Finally, increment the value.
browser[version]++;
}
Note that you were using ===
where you should've been using =
(in === {}
).
Let's explain this line:
const browsers = data.browsers = data.browsers || {};
The last part: data.browsers = data.browsers || {}
sets data.browsers
to be itself if it exists. If it doesn't yet, it's set to be a new empty object.
Then, that whole value gets assigned to browsers
, for ease of access.
Now, shorter code shouldn't be top priority, but in cases like this, you can make the code a lot more readable.
Upvotes: 6
Reputation: 17940
You can give up the if statements and do it like this:
uap.browser = uap.browser || {}
essentially it does the same as the if only much shorter
Upvotes: 2