TaylorMac
TaylorMac

Reputation: 9002

Find object by properties from array

With an array, a value, and and an object with nested objects:

Object

mesh

Array

['options', 'range', 'x']

Value

12.5

Is it possible to translate this to update a property, e.g.

mesh.options.range.x = 12.5

Attempted:

index = (obj, i) ->
   obj[i]

arr.reduce(index, obj) = 12.5

Update

Thank you all for the elegant solutions.

Upvotes: 0

Views: 75

Answers (4)

cookie monster
cookie monster

Reputation: 10972

Using .reduce() is actually pretty nice for this:

// current object----|    |----current key
//                   v    v
arr.reduce(function(obj, key) {
    return obj == null ? obj : obj[key];
}, window.mesh);
//        ^
//        |-- initial object

Your attempt to use .reduce() needed to pass a function that manages the "accumulation".

Here, as long as the previous obj wasn't null or undefined, it'll return the key of the current obj, which becomes the next obj.


Then since you need to assign a value, you'd actually want to get the value of the second to last key.

var o = arr.slice(0,-1).reduce(function(obj, key) {
    return obj == null ? obj : obj[key];
}, window.mesh);

And then check its existence and use the last item in arr to do the assignment.

o && o[arr.pop()] = 12.5;

All of this can be abstracted away into a function that does one or the other based on how many arguments were passed.

function setFromArray(obj, arr, val) {
    var keys = arguments.length < 3 ? arr.slice() : arr.slice(0, -1);

    var o = keys.slice(0,-1).reduce(function(obj, key) {
        return obj == null ? obj : obj[key];
    }, window.mesh);

    if (arguments.length < 3)
        return o;
    else 
        o && o[keys.pop()];
}

Upvotes: 2

Dan Smolinske
Dan Smolinske

Reputation: 319

If array length is static do something like this:

mesh[array[0]][array[1]][array[2]] = value;

However, one problem with this is that javascript doesn't do autovivification, so if you're accessing a key value that isn't previously defined you could run into errors (if mesh.options hasn't been defined then the above will throw an error because you can't assign to it). To solve that you might abstract this out into a function that handles things recursively:

http://jsfiddle.net/h4jVg/

function update_val(obj, array, val, prev) {
    if (array.length == 0) {
        obj = val;
        return;
    }

    var cur = array.shift();

    if(array.length == 0) {
        obj[cur] = val;
        return;
    } else if (obj[cur] == undefined) {
        obj[cur] = {};
    }

    update_val(obj[cur], array, val);
}

Upvotes: 0

Alexis King
Alexis King

Reputation: 43842

Here's a general solution:

function setPropertyPath(obj, path, value) {
    var o = obj;
    for (var i = 0; i < path.length - 1; i++) {
        o = o[path[i]];
    }
    o[path[path.length - 1]] = value;
}

Usage:

var obj = { a: { b: { c: 0 } } };
setPropertyPath(obj, ['a', 'b', 'c'], 10);
console.log(obj.a.b.c); // prints '10'

JSBin

Upvotes: 2

Josh Beam
Josh Beam

Reputation: 19772

var mesh = {},
    arr = ['options','range','x'],
    value = 12.5;

mesh[arr[0]][arr[1]][arr[2]] = value;

Upvotes: 0

Related Questions