8

I am trying to deeply assign a value in an object. For example:

const errors = {}
if(errorOnSpecificField) {
  // TypeError: Cannot read property 'subSubCategory' of undefined(…)
  errors.subCategory.subSubCategory.fieldWithError = 'Error Message'
}

Right now, without lodash, I can do:

const errors = {}
if(errorOnSpecificField) {
    errors.subCategory = errors.SubCategory || {}
    errors.subCategory.subSubCategory = errors.SubCategory.subSubCategory || {}
    errors.subCategory.subSubCategory.fieldWithError = 'Error Message'
}

With lodash, I can do this:

const errors = {}
if(errorOnSpecificField) {
    _.set(errors, 'subCategory.subSubCategory.fieldWithError', 'Error Message');
}

I am trying to avoid using a third party library. Is there a more elegant solution, especially now that es2015 has object destructuring. The inverse operation is easy:

  let {subCategory : {subSubCategory: {fieldWithError}}} = errors

What is an elegant solution to deep object assignment? Thanks!

5
  • 2
    Object.assign() ? - it overrides all properties though Commented Jun 15, 2016 at 19:04
  • As you mention, because it overrides the other properties already set, this won't work for my case Commented Jun 15, 2016 at 19:12
  • you might then look for some of the many extend implementations Commented Jun 15, 2016 at 19:13
  • Uh, why not just use errors.subCategory = {subSubCategory: {fieldWithError: 'Error Message'}}? It's the exact reverse of the destructuring. Commented Jun 15, 2016 at 19:33
  • The problem is that if I already have other subSubCategories defined, it will overwrite them Commented Jun 15, 2016 at 19:39

4 Answers 4

0

Here's a fairly readable way to safely assign to the deep object:

(((errors||{}).subCategory||{}).subSubCategory||{}).fieldWithError = 'Error Message'

That doesn't create errors.subCategory.subSubCategory if it doesn't already exist, though.

Sign up to request clarification or add additional context in comments.

2 Comments

This actually doesn't work for assignment. For accessing, yes, but not for creating properties.
Hmm... It won't create errors.subCategory.subSubCategory, but it will safely assign to it if it already exists.
0

Short answer, no there is no clean way of doing this without writing a method for it (tbh you could just use the method from lodash without importing the whole library)

... however ...

WARNING This is for fun only. Do not try this in production (req es6).

Object.prototype.chainSet = function() {
  let handler = {
    get (target, name) {
      if (!(name in target)) {
        target[name] = new Proxy({}, handler)
      }
      return target[name]
    }
  }

  return new Proxy(this, handler)
}

use:

let a = {}
a.chainSet().foo.bar.baz = 1
a.foo.bar.baz // => 1

Comments

0

Object.assign() will work just fine for what you're asking.

let errors = { otherField: "value" };
let newobj = {subCategory : {subSubCategory: {fieldWithError: "Error goes here"}}};
Object.assign(errors, newobj);

This yields:

{
  otherField:'value',
  subCategory: {
    subSubCategory: {
      fieldWithError:'Error goes here'
    }
  }
}

1 Comment

if errors = {subCategory: {x: "y"}} and you assign newobj, then property x gets "removed" (the whole subCategory object is swapped out). OP is looking for a way to avoid that.
0

You could try something like below:

function ErrorRegistry(obj)
{
    this.errors = obj || {};
    this.addError = function(k, msg)
    {
        var keys = k.split('.');
        var o = this.errors;
        for(var i = 0, l = keys.length, last = l-1; i<l; i++)
        {
            if(typeof o[keys[i]] === 'undefined')
                o[keys[i]] = {};
            if(i == last)
                o[keys[i]] = msg;
            else
                o = o[keys[i]];
        }
    };
}
var errors = {'subCategory1':{'fieldWithError1':'Error1'}};
var errorRegistry = new ErrorRegistry(errors);
errorRegistry.addError('subCategory1.fieldWithError2', "Error2");
errorRegistry.addError('subCategory1.subSubCategory1.fieldWithError3', "Error3");
errorRegistry.addError('subCategory1.subSubCategory2.fieldWithError4', "Error4");
errors = errorRegistry.errors;
console.log(errors);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.