1

I have an easy dot notation getter function and I would love to have a setter that works in the same way. Any ideas?

var person = {
    name : {
       first : 'Peter',
       last  : 'Smith'
    }
};

// ---

var dotGet = function(str, obj) {

    return str.split('.').reduce(function(obj, i) {
        return obj[i];
    }, obj);

};

var dotSet = function(str, value, obj) {

    // updated, thx to @thg435
    var arr = str.split('.');

    while (arr.length > 1) {
        obj = obj[arr.shift()];
    }

    obj[arr.shift()] = value;

    return obj;

}

// returns `Peter`
var a = dotGet('person.name.first', person);

// should set `person.name.first` to 'Bob'
var b = dotSet('person.name.first', 'Bob', person);
Machavity
  • 30,841
  • 27
  • 92
  • 100
ezmilhouse
  • 8,933
  • 7
  • 29
  • 38
  • 1
    Is there a reason you dont use the native JS as follows `console.log(person.name.first);` and `person.name.first = 'Bob';` ? – Dmitry Kudryavtsev Jun 14 '12 at 12:01
  • 2
    see http://stackoverflow.com/questions/10934664/convert-string-in-dot-notation-to-get-the-object-reference and many others – georg Jun 14 '12 at 12:02
  • @skwee I use get/set to access private vars in class, code above is just a simplification – ezmilhouse Jun 14 '12 at 12:09

2 Answers2

3
var set = function (exp, value, scope) {
  var levels = exp.split('.');
  var max_level = levels.length - 1;
  var target = scope;
  levels.some(function (level, i) {
    if (typeof level === 'undefined') {
      return true;
    }
    if (i === max_level) {
      target[level] = value;
    } else {
      var obj = target[level] || {};
      target[level] = obj;
      target = obj;
    }
  });
};

You can check out my expression compiler that does what you need and more.

The usage is:

var scope = {};
set('person.name', 'Josh', scope);

scope.person.name === 'Josh'
J. K.
  • 8,268
  • 1
  • 36
  • 35
  • Why the `some` and `if (typeof level === 'undefined') { return true; }` level being an exploded string this will never be the case – Tofandel Aug 05 '22 at 11:13
-1

Try this:

var dotSet = function(str, value, obj) {
  var keys = str.split('.');
  var parent = obj;

  for (var i = 0; i < keys.length - 1; i++) {
    var key = keys[i];

    if (!(key in parent)) {
      parent[key] = {};
      parent = parent[key];
    }
  }

  parent[keys[keys.length - 1]] = value;
}

var person = {};
dotSet('person.name.first', 'Bob', person);

It produces this object:

{ person: { name: { first: 'Bob' } } }

ioseb
  • 16,625
  • 3
  • 33
  • 29
  • There is a bug somewhere in this implementation. Replacing dotSet with Jan's solution worked as expected. Running the following: ``` var obj = {}; dotSet('0.first_key', [0,1,2], obj); dotSet('0.second_key', [3,4,5], obj); // obj => { '0': { first_key: [ 0, 1, 2 ] }, second_key: [ 3, 4, 5 ] } ``` – Glavin001 May 29 '16 at 21:12