views:

29

answers:

2

I've had trouble with people jumping to conclusions about what I need here, so please read this and think about it before answering.

Here is the case:

You have an incoming object. You do not know the structure of this object. You do however have a "target" to something in the object. So let's pretend there is myObject, and you have some kind of target defined like an array of association levels:

var objectTarget = [ 'firstLevel', 'secondLevel' ,'targetProperty' ];

Now the incoming myObject looks like this:

{
    firstLevel: {
        secondLevel: {
            targetProperty: "The goods!"
        }
    }
}

But as stated before, you don't know the structure. All you know is what is in the objectTarget array.

My problem is being able to address an arbitrary location within an object based solely off a target. If I knew that the target would always be three levels deep, then I could simply reference it like this:

myObject[objectTarget[1]][objectTarget[2]][objectTarget[3]];

However, because I cannot be sure of the number of level depth, this is not adequate. The only way I have been able to accomplish this task is choose a maximum number of reasonable levels, and then switch on it. Like so:

switch ( objectTarget.length) {
    case 1:
        var result = myObject[objectTarget[1]];
        break;
    case 2:
        var result = myObject[objectTarget[1]][objectTarget[2]];
        break;
    case 3:
        var result = myObject[objectTarget[1]][objectTarget[2]][objectTarget[3]];
        break;
    case 4:
        var result = myObject[objectTarget[1]][objectTarget[2]][objectTarget[3]][objectTarget[1]];
        break;
}

..etc

This is obviously extremely messy, and not the optimal solution.

Does this properly explain my problem? Is there a cleaner manner in which to accomplish this?

Thank you in advance for any advice you can provide.

+1  A: 

An attempt off the top of my head:

function findTarget(obj, targets) {
    for(var i = 0; i < targets.length; i++) {
      var prop = targets[i];
      if(obj[prop] != undefined) {
        obj = obj[prop];
      } else {
        return undefined; // Whatever you want when the target does not exist

        // or, if it's useful to you
        return obj; // Maximum reachable target
      }
    }

    return obj;
}

var target = findTarget(incoming, ['level1', 'level2', ...]);

if(target == undefined) {
  // couldn't traverse the entire target list...
}
Jesse Dhillon
Thank you! Side-question: How slow would you guess this is?
Spot
I would not think that this is noticeably slower than if you knew the path ahead of time. Each `.` in something like `obj.leve1.level2.target` requires a runtime lookup so you don't gain anything with foreknowledge. Although for each depth traversed, you would need an additional access to get the name of the target using this method (the `targets[i]` part.) I wouldn't be worried about the speed of this code, unless `targets.length` gets large.
Jesse Dhillon
+1  A: 

Another approach if you can use (or include for IE) the reduce Array method:

function getTarget(obj, target) {
  return target.reduce(function(a, b) { return a && a[b]; }, obj);
}

// usage (assuming that `myObject` is your sample object):
var target = ['firstLevel', 'secondLevel' ,'targetProperty'];
getTarget(myObject, target); // "The goods!"
CMS