Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pluck specific javascript value from an object based on an array of indexes

Given a nested object like this:

var cars = {
    "bentley": {
        "suppliers": [
            {
            "location": "England",
            "name": "Sheffield Mines"}
        ]
        // ...
    }
};

and an array like this ["bentley", "suppliers", "0", "name"], is there an existing function that will pluck the deepest element, i.e. pluck_innards(cars, ['bentley', "suppliers", "0", "name"]) and that returns "Sheffield Mines".

In other words, is there a function (which I will name deep_pluck) where

deep_pluck(cars, ['bentley', 'suppliers', '0', 'name']) 
         === cars['bentley']['suppliers']['0']['name']

It seems to me that this is simple, yet common enough, to have probably been done in one of the Javascript utility libraries such as jQuery or lo-dash/underscore - but I have not seen it.

My thought is something trivial, along the lines of:

function deep_pluck(array, identities) {
    var this_id = identities.shift();
    if (identities.length > 0) {
        return deep_pluck(array[this_id], identities);
    }
    return array[this_id];
}

Which I have posted on jsFiddle.

It would be helpful of course if the function were smart enough to identify when numerical indexes in arrays are needed. I am not sure offhand what other caveats may be a concern.

This is all a fairly long question for something I imagine has already been cleverly solved, but I thought to post this as I would interested in seeing what solutions are out there.

like image 556
Brian M. Hunt Avatar asked Oct 08 '12 16:10

Brian M. Hunt


3 Answers

I don't think you'll have problems with Array indexes if you pass them as number 0.

Here's alternative version of your function without recursion:

function deep_pluck(object, identities) {
    var result = object;
    for(var i = 0; i < identities.length; i++) {
        result = result[identities[i]];
    }
    return result;
}

Working example here: http://jsfiddle.net/AmH2w/1/

like image 147
Inferpse Avatar answered Nov 10 '22 07:11

Inferpse


dotty.get(obj, pathspec) does it, accepting either an array or a dotted string as the pathspec.

Dotty is open source, and also has an exists method, and a putter.

The methodology is recursion and very similar to your idea, except that dotty includes a test for null/undefined objects so that it doesn't throw exceptions for trying to access an element of something that doesn't exist.

The dotty.get() source from the docs is posted below:

var get = module.exports.get = function get(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return;
  }

  if (path.length === 0) {
    return object[key];
  }

  if (path.length) {
    return get(object[key], path);
  }
};
like image 1
Paul Avatar answered Nov 10 '22 08:11

Paul


Although not a generic library, it seems that CasperJS has something of this kind with its utils.getPropertyPath function.

/**
 * Retrieves the value of an Object foreign property using a dot-separated
 * path string.
 *
 * Beware, this function doesn't handle object key names containing a dot.
 *
 * @param  Object  obj   The source object
 * @param  String  path  Dot separated path, eg. "x.y.z"
 */
function getPropertyPath(obj, path) {
    if (!isObject(obj) || !isString(path)) {
        return undefined;
    }
    var value = obj;
    path.split('.').forEach(function(property) {
        if (typeof value === "object" && property in value) {
            value = value[property];
        } else {
            value = undefined;
        }
    });
    return value;
}

Edit:

I have come across implementations to solve this a couple times since, including:

  1. the getObject plugin by Ben Alman (on Github).
  2. one I rolled - see gist

Edit (2014)

I would also note the relatively new lodash.deep.

like image 1
Brian M. Hunt Avatar answered Nov 10 '22 07:11

Brian M. Hunt