Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference Javascript object from string

Tags:

javascript

Given data similar to:

var data = [{id: 12345, 
    name:'my products', 
    items:[{ 
               size: 'XXL', 
               sku: 'awe2345', 
               prices:[{type: 'rrp',prices: 10.99}, 
                        {type: 'sell_price', price: 9.99}, 
                         {type:'dealer', price:4.50} 
               ] 
               },{ 
               size: 'XL', 
               sku: 'awe2346', 
               prices:[{type: 'rep', prices: 10.99}, 
                          {type: 'sell_price', price: 9.99}, 
                          {type:'dealer', price:4.50} 
               ] 
               } 
       ] 
   }] 
}]

is there a way to evaluate a string representation of an element in the data object? for example: "data[0].items[0].prices[0].rrp" ...without using eval()?

like image 678
Alistair Avatar asked Feb 24 '23 09:02

Alistair


1 Answers

The ideal solution would be to not have the string representation in the first place. Seriously ask yourself whether you change the existing code to give you the content in a more ideal output.

However, if you can't, this should do what you want:

var path = "data[0].items[0].prices[0].rrp".split(/[\[\]\.]+/);
var next = window;

if (path[path.length - 1] == "") {
    path.pop();
};

while (path.length && (next = next[path.shift()]) && typeof next == "object" && next !== null);

next;

Create a function for this:

function get(path) {
    var next = window;

    path = path.split(/[\[\]\.]+/);

    if (path[path.length - 1] == "") {
        path.pop();
    };

    while (path.length && (next = next[path.shift()]) && typeof next === "object" && next !== null);

    return path.length ? undefined : next;
}

Downsides:

  1. The variable data must be in the global scope for this to work (cannot be a local variable).

  2. It's skanky. Use it as a last resort.

Edit: To use setting functionality, you can abuse the pass-by-reference nature of JavaScript objects as follows:

function set(path, value) {
    var split = Math.max(path.lastIndexOf("["), path.lastIndexOf("."));

    get(path.slice(0, split))[path.slice(split + 1).replace(/\]/, "")] = value;
}

To provide some explanation of how this works:

The getter first splits the input into an array, so that each element is a member we need to traverse [data, 0, items, 0, prices, 0, rrp]. If the search string ends with a "]", we get an extra empty-element at the end of the array, so we check for that and remove it.

We then do the big loop; so whist we have elements to traverse (while path.length), set the next variable to the next object member we need to traverse next = next[path.shift()]. Check that it's an object (otherwise it wont have any members to traverse), and check that it isn't null (because typeof null == "object").

Once the loop has executed, we'll have the final element in the chain; so return it.

The setter searches for the last object reference in the search string, and retrieves that object reference using the get() function. It then sets the returned objects key to the desired value. If we hadn't done it this way, we'd have been setting a value rather than a reference, so the "real" object would never get updated.

like image 147
Matt Avatar answered Mar 06 '23 12:03

Matt