1

Let's say, I have the following JSON, which can be easily converted back and forth to a JavaScript object:

{
    "foo": {
        "bar": "Common substitute word",
        "baz": "Another common substitute word",
        "questionWords": {
            "wat": "Inadequate question word",
            "wut": "Even more inadequate question word"
        }
    }
}

And I receive modifications regarding this JSON in another JSON file, like this:

{
    "foo.questionWords.wut": "A question word to avoid"
}

So the path to modify is given as a string. And I have to modify the first JSON by the new data.

But also the new data path possibly does not exist:

{
    "foo.callingWords.dude": "Commonly used synonym for pal"
}

And the new data path might have indeterminable depth:

{
    "who.knows.how.deep.we.will.go": "Look, a penny!"
}

What is the best way to handle this without a JS library, just by plain Vanilia JS?

(You can use the latest JavaScript features.)

Thank you for your help!

2

5 Answers 5

7

Never use eval for tasks like this. Split your "path" and walk step-by-step:

var data = {
    "foo": {
        "bar": "Common substitute word",
        "baz": "Another common substitute word",
        "questionWords": {
            "wat": "Inadequate question word",
            "wut": "Even more inadequate question word"
        }
    }
},

mods = {
  "foo.questionWords.wut": "A question word to avoid",
  "foo.callingWords.dude": "Commonly used synonym for pal",
  "who.knows.how.deep.we.will.go": "Look, a penny!"
};

function apply(data, mods) {
  for (var path in mods) {
    var k = data;
    var steps = path.split('.');
    var last = steps.pop();
    steps.forEach(e => (k[e] = k[e] || {}) && (k = k[e]));
    k[last] = mods[path];
  }
  return data
}

console.log(apply(data, mods))

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

3 Comments

this line is the killer steps.forEach(e => (k[e] = k[e] || {}) && (k = k[e])); thanks for it!
@balexandre, hope it helps, not kills =))
it killed me :P I had 5 more lines to do the same :D
0

One of the options in vanillaJS, you can use eval (but be afraid, very afraid!) like this:

var t = {
    "foo": {
        "bar": "Common substitute word",
        "baz": "Another common substitute word",
        "questionWords": {
            "wat": "Inadequate question word",
            "wut": "Even more inadequate question word"
        }
    }
};

eval("t.foo.bar = 13")

enter image description here

Comments

0

Essentially the same as everybody's answer, just avoiding usage of eval(), if that could be a problem.

var obj={
    "foo": {
        "bar": "Common substitute word",
        "baz": "Another common substitute word",
        "questionWords": {
            "wat": "Inadequate question word",
            "wut": "Even more inadequate question word"
        }
    }
}

var obj2={
    "foo.questionWords.wut": "A question word to avoid"
}

for(var i in obj2){
	  var res = i.split(".");
    var fieldName = res.splice(res.length-1,1);
    var objField = res.reduce((r, u) => r && r[u] ? r[u] : '' , obj);
    objField[fieldName]=obj2[i];
}
console.log(obj);

Comments

0

My solution using recursive function.

const modifyObject = (object, jsonPath, value) => {
  const keys = jsonPath.split(".");
  const key = keys.splice(0, 1);
  if (keys.length > 0) {
    modifyObject(object[key], keys.join('.'), value)
  } else {
    object[key] = value;
  }
}

const obj = {
  foo: {
    bar: 11,
    baz: 12,
    bac: {
      leng: 1,
      str: 'hello world'
    }
  }
};

modifyObject(obj, 'foo.bac.leng', 'modified');

console.log(JSON.stringify(obj));

Comments

0

In imperative style where I'm just mutating source:

updateAll = function(source, target) {
    Object.keys(target)
        .forEach((k) => update(source, k, target[k]));
}

update = function(source, targetKey, targetValue) {
var keys = targetKey.split('.');

    // Iterate as long as there are keys to shift out and as long as source is
    // defined for that key.
     while((key = keys.shift()) && source[key]) {

        // If we're at a leaf, assign new value. Otherwise, iterate further into
        // the object.
        if (keys.length === 0 && typeof source[key] !== 'object') {
            source[key] = targetValue;
        } else {
            source = source[key];
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.