0

I note the following similarity to this post: Dynamic deep setting for a JavaScript object

However, the above post is based upon a known structure and depth to the javascript object and not truly dynamic. Truly dynamic would suggest that you did not have any precursor knowledge of the structure, just a path and a value to replace it with. I have created a fairly good use case over on JSFiddle here:

http://jsfiddle.net/kstubs/nJrLp/1/

function Message(message) {
$('result').insert('<div>' + message + '</div>');
}

var obj = {
"array": [1, 2, 3],
"boolean": true,
"null": null,
"number": 123,
"object": {
    "a": "b",
    "c": "d",
    "e": "f",
    "complex_array1": [{
        "g": "h"
    }, {
        "bingo": "bongo"
    }, {
        "x": {
            "complex_array2": [{
                "h": "i"
            }, {
                "j": "k"
            }, {
                "bingo": "bongo"
            }, {
                "bango": "jango"
            }]
        }
    }]
},
"string": "Hello World"
};

var list = [{
"h": "i"
}, {
"j": "k"
}];

function walk(path,value) {
var a = path.split('.');
var context = obj;

for (i = 0; i < a.size(); i++) {
    context = context[a[i]];
}

}

The use case:

  1. Find complex_array2
  2. Update its list to a new list (a new array)

The new array is the array list which should replace the list for complex_array2. The javascript function walk does just that, walks the javascript object until the path criteria is met and then sets the value to whatever value is passed to the walk function, however the new value does not stick.

I know why it doesn't stick, because as you walk over an object of type array you lose the pointer to the original object. So, the challenge is to walk the javascript object and not lose context of the original object.

Thanks for any assistance.

Karl..

Community
  • 1
  • 1
kstubs
  • 808
  • 4
  • 18
  • So....what's the question? Do you really need it or you just want to challenge SO users? What have you tried? – Aurelio De Rosa Sep 09 '13 at 20:32
  • var context = obj, old=obj; ... old[a[i]]="whatever" – dandavis Sep 09 '13 at 20:33
  • This could be of interest -- one of my own answers to a slightly different flavour of question -- http://stackoverflow.com/questions/12163160/searching-json-object-key-containing-value-then-unset-it/12170015#12170015 – Pebbl Sep 09 '13 at 20:43
  • Yes @AurelioDeRosa it is really needed. I have a complex javascript object which is dynamically bound to a web form. The object is stored in a text field as a Json string. – kstubs Sep 09 '13 at 20:45

3 Answers3

1

Just loop over all but the last element in the path. Then the final element is used for assignment after the loop.

var i = 0;

for (; i < a.size() - 1; i++) {
    context = context[a[i]];
}

context[a[i]] = value;

Technically you can leave the declaration of i inside the for. I just find this clearer.

http://jsfiddle.net/nJrLp/2/

James Montagne
  • 77,516
  • 14
  • 110
  • 130
  • Exactly! I came up with the same answer: http://jsfiddle.net/kstubs/nJrLp/3/ based on Andrew's remarks. – kstubs Sep 09 '13 at 21:28
  • It's unrelated to this, but the way you are using `i` without declaring it first with `var` makes it a global variable. You really shouldn't do that. – James Montagne Sep 09 '13 at 23:25
1

The reason your code doesn't work as it's written is because rather than changing a property on an object you have a reference to, you're actually changing which object your local variable points to.

context = context[a[i]];

context is a pointer to an object, and it's a local variable. When you assign to it, you're assigning a new pointer value, which loses the reference to the previous object. If you want to replace it, you'll have to refer to it from its parent object. Assume parent is one such object; once you locate your target object's key name (let's say you've put it in variable key), you could overwrite the existing value as such:

parent[key] = new_value;

This will dereference parent, find the property named by key, and replace its value (which is a pointer) with the memory address of new_value. What you have currently works something like this:

var context = parent[key];
context = new_value;

In this case you're simply changing the pointer value of the local variable context, not the object that parent[key] points to.

Andrew Noyes
  • 5,248
  • 1
  • 18
  • 14
  • Like this: http://jsfiddle.net/kstubs/nJrLp/3/ Its seem this is working, which is fantastic. Plugging this into the real solution and will report any other issues I may find. – kstubs Sep 09 '13 at 21:26
0

I used a helper function for reading complex json objects. (http://jsfiddle.net/JBBAJ/)

var object = {
    data: {
        users: [
            {
                firstName: "White"
            },
            {
                firstName: "Black"
            }
        ]
    }
}
var read = function(path, obj) {
    var path = path.split(".");
    var item = path.shift();
    if(item.indexOf("]") == item.length-1) {
        // array
        item = item.split("[");
        var arrayName = item.shift();
        var arrayIndex = parseInt(item.shift().replace("]", ""));
        var arr = obj[arrayName || ""];
        if(arr && arr[arrayIndex]) {
            return read(path.join("."), arr[arrayIndex]);
        } else {
            return null;
        }
    } else {
        // object
        if(obj[item]) {
            if(path.length === 0) {
                return obj[item];
            } else {
                return read(path.join("."), obj[item]);
            }
        } else {
            return null;
        }
    }

}
console.log(read("data.users[0].firstName", object)); // White
console.log(read("data.users[1].firstName", object)); // Black
console.log(read("data.test.users[0]", object)); // null

The function read accepts a path and an object.

Krasimir
  • 13,306
  • 3
  • 40
  • 55