最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

node.js - JavaScript: Adding a nested property to an object that may not exist - Stack Overflow

programmeradmin0浏览0评论

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?

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?

Share Improve this question edited Aug 28, 2017 at 11:42 roryok asked Aug 28, 2017 at 11:11 roryokroryok 9,65519 gold badges81 silver badges139 bronze badges 4
  • 1 There is already an excellent library for such operations - lodash. It has get method which does what you need: _.set(object, 'a[0].b.c', 4); – alexmac Commented Aug 28, 2017 at 11:14
  • Use Object.prototype.hasOwnProperty() instead of typeof... – Denis Giffeler Commented Aug 28, 2017 at 11:18
  • @DenisGiffeler that seems like a longer way of doing things. I'm trying to shorten my code – roryok Commented Aug 28, 2017 at 11:41
  • @alexmac I want to avoid using lodash or any other library for this - looking for neater vanilla JS – roryok Commented Aug 28, 2017 at 11:41
Add a ment  | 

3 Answers 3

Reset to default 6

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.

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

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));
发布评论

评论列表(0)

  1. 暂无评论