test the existence of property in a deep object structure

前端 未结 6 1297
悲&欢浪女
悲&欢浪女 2021-01-11 23:13

In javascript, lets say I want to access a property deep in an object, for example:

entry.mediaGroup[0].contents[0].url

At any point along that structure, a

相关标签:
6条回答
  • 2021-01-11 23:51

    Here's the one i have been using for a while

       var obj = { a: { b: [
                            { c: {d: 'XYZ'} }
                        ] } };
    
        // working
    
        obj.a.b[0].c.d = null;
        console.log('value:'+getProperty(obj, 'a.b[0].c.d', 'NOT-AVAILABLE')); // value:null
    
        obj.a.b[0].c.d = 'XYZ';
        console.log('value:'+getProperty(obj, 'a.b[0].c.d', 'NOT-AVAILABLE')); // value:XYZ
        console.log('value:'+getProperty(obj, 'a.b[0].c.d.k.sds', 'NOT-AVAILABLE')); // value:NOT-AVAILABLE
    
        obj.a.b[0].c = null;
        console.log('value:'+getProperty(obj, 'a.b[0].c.d', 'NOT-AVAILABLE'));  // value:NOT-AVAILABLE
    
    
        // will not work
        //console.log('v:'+getProperty(obj, 'a.b["0"].c.d'));
    

    Here's the function

    function getProperty(obj, str, defaultValue){
    
        var props = str.split('.').map(function(prop){
            var arrAccessRegEx = /(.*)\[(.*)\]/g;
            if (arrAccessRegEx.test(prop)){
                return prop.split(arrAccessRegEx).filter(function(ele){return ele!=''; });
            } else {
                var retArr = [];
                retArr.push(prop);
                return retArr
            };
        });
    
        //console.log(props);
    
        for(var i=0;i<props.length;i++){
            var prop = props[i][0];
    
            //console.log('prop:'+prop);
    
            if (obj === null) return defaultValue;
    
            obj = obj[prop];
    
            if (obj === undefined) return defaultValue;
    
            if (props[i].length == 2){
                var idx = props[i][1];
                if (!(obj instanceof Array)) return defaultValue;
                if (idx < obj.length ){
                    obj = obj[idx];
                    if (obj === undefined) return defaultValue;
                }
            }
    
        } // for each item in split
    
        return obj;
    }
    
    0 讨论(0)
  • 2021-01-11 23:52

    There are probably 3-4 different questions along this vein, and four times as many answers. None of them really satisfied me, so I made my own, and I'll share it.

    This function is called "deepGet".

    Example:

    deepGet(mySampleData, "foo.bar[2].baz", null);

    Here is the full code:

    function deepGet (obj, path, defaultValue) {
    
        // Split the path into components
        var a = path.split('.');
    
        // If we have just one component left, note that for later.
        var last = (a.length) === 1;
    
        // See if the next item is an array with an index
        var myregexp = /([a-zA-Z]+)(\[(\d+)\])+/; // matches:  item[0]
        var match = myregexp.exec(a[0]);
    
        // Get the next item
        var next;
        if (match !== null) {
            next = obj[match[1]];
            if (next !== undefined) {
                next = next[match[3]];
            }
        } else {
            next = obj[a[0]];
        }
    
        if (next === undefined || next === null) {
            // If we don't have what we want, return the default value
            return defaultValue;
        } else {
            if (last) {
                // If it's the last item in the path, return it
                return next; 
            } else { 
                // If we have more items in the path to go, recurse
                return deepGet (next, a.slice(1).join("."), defaultValue); 
            }
        }
    }
    

    Here is a jsFiddle: http://jsfiddle.net/7quzmjh8/2/

    I was inspired by these two things:

    http://designpepper.com/blog/drips/making-deep-property-access-safe-in-javascript.html http://jsfiddle.net/wxrzM/1/

    Hopefully this is useful to someone out there :)

    0 讨论(0)
  • 2021-01-11 23:53

    This is a very lazy way to do it, but it meets the criteria for many similar situations:

    try {
      console.log(entry.mediaGroup[0].contents[0].url);
    } catch (e) {}
    

    This should not be done on long code blocks where other errors may potentially be ignored, but should be suitable for a simple situation like this.

    0 讨论(0)
  • 2021-01-11 23:58
    /*decend through an object tree to a specified node, and return it.
      If node is unreachable, return undefined. This should also work with arrays in the tree.                                                                                               
      Examples:                                                                                                                                                                            
        var test1 = {a:{b:{c:{d:1}}}};                                                                                                                                            
        console.log(objectDesend(test1, 'a', 'b', 'c', 'd'));                                                                                                                
        var test2 = {a:{b:{c:1}}};     //will fail to reach d                                                                                                                                         
        console.log(objectDesend(test2, 'a', 'b', 'c', 'd'));
    */
    var objectDescend = function(){
        var obj = arguments[0];
        var keys = arguments;
        var cur = obj;                                                                                                                                                        
        for(var i=1; i<keys.length; i++){                                                                                                                                     
            var key = keys[i];                                                                                                                                                
            var cur = cur[key];                                                                                                                                               
            if(typeof(cur)=='undefined')                                                                                                                                      
                return cur;                                                                                                                                                   
        }                                                                                                                                                                     
        return cur;                                                                                                                                                           
    }                                                                                                                                                                         
    
    var test1 = {a:{b:{c:{d:1}}}};                                                                                                                                            
    console.log(objectDescend(test1, 'a', 'b', 'c', 'd'));                                                                                                                
    var test2 = {a:{b:{c:1}}};                                                                                                                                              
    console.log(objectDescend(test2, 'a', 'b', 'c', 'd'));
    

    So this will return either the value you are looking for, or undefined since that value doesn't exist. It won't return false, as that may actually be the value you are looking for (d:false).

    In my code base, I add Object.prototype.descend, so I can do test1.descend('a', 'b', 'c', 'd'). This will only work in ECMAScript 5 (IE>=9) since you need to make it so your function doesn't appear in enumerations. For more info: Add a method to Object primative, but not have it come up as a property

    Here is my code for that:

    Object.defineProperty(Object.prototype, 'descend', {
        value: function(){
            var keys = arguments;
            var cur = this;
            for(var i=0; i<keys.length; i++){
                var key = keys[i];
                var cur = cur[key];
                if(typeof(cur)=='undefined')
                    return cur;
            }
            return cur;
        }
    });
    
    
    
    var test1 = {a:{b:{c:{d:false}}}};
    //this will return false, which is the value of d                                                                                   
    console.log(test1.descend('a', 'b', 'c', 'd'));                                                                                                                       
    var test2 = {a:{b:{c:1}}};
    //undefined since we can't reach d.                                                                                                
    console.log(test2.descend(test2, 'a', 'b', 'c', 'd'));
    
    0 讨论(0)
  • 2021-01-12 00:09

    Your current solution is probably as good as you can get, as mVChr says, try..catch is just lazy here. It's probably far less effient and has nothing to recommend it other than perhaps being easier to type (but not significantly so) and it'll be harder to debug as it silently hides errors.

    The real issue is the very long "reference worm" created by attempting such access. An alternative to the original that at least reduces the number of property lookups is:

    var o;
    if ( (o = entry       ) &&
         (o = o.mediaGroup) &&
         (o = o[0]        ) &&
         (o = o.contents  ) &&
         (o = o[0]        )) {
      alert(o.url);
    }
    

    But I expect you won't like that.

    If you have many such deep access paths, you might like to create a function to do the access and return the last object on success or some other vaule on failure. For failure, you could also have it return the last non-falsey object on the path.

    // Create test object
    var entry = {};
    entry.mediaGroup = [{
      contents: [{url: 'url'}]
    }];
    
    // Check that it "works" 
    // alert(entry.mediaGroup[0].contents[0].url);
    
    
    // Deep property access function, returns last object
    // or false
    function deepAccess(obj) {
    
      var path = arguments;
      var i = 0, iLen = path.length;
      var o = path[i++];  // o is first arg
      var p = path[i++];  // p is second arg
    
      // Go along path until o[p] is falsey
      while (o[p]) {
        o = o[p];
        p = path[i++];
      }
    
      // Return false if didn't get all the way along
      // the path or the last non-falsey value referenced
      return (--i == iLen) && o;
    }
    
    // Test it    
    var x = deepAccess(entry, 'mediaGroup','0','contents','0');
    alert(x && x.url);  // url
    
    var x = deepAccess(entry, 'mediaGroup','1','contents','0');
    alert(x && x.url);  // false
    
    0 讨论(0)
  • 2021-01-12 00:10

    I use this simple function for playing around with deep object properties:

    getProperty = function(path) {
        try {
            return eval(path);
        }
        catch (e) {
            return undefined;
        }
    };
    

    Here's an example:

    var test = {a:{b:{c:"success!"}}};
    
    alert(getProperty('test.c.c'));
    // undefined
    
    alert(getProperty('test.a.b.c'));
    // success!
    
    0 讨论(0)
提交回复
热议问题