Created
June 13, 2015 20:25
-
-
Save kbjr/84d14c0b096bdd56d03c to your computer and use it in GitHub Desktop.
Find Object Scope
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Finds a specified property path in a nested object structure | |
// | |
// @param {obj} the object to search | |
// @param {path} the property path (eg. "foo.bar.[0].{baz:qux}.property") | |
// @return object | |
// | |
function findObjectScope(obj, path) { | |
var scope = obj; | |
var steps = path.split('.'); | |
var property = steps.pop(); | |
var current; | |
var parsed; | |
try { | |
while (current = steps.shift()) { | |
parsed = parseProperty(current); | |
parsed = stepDownKey(scope, parsed); | |
scope = scope[parsed]; | |
} | |
current = property; | |
property = parseProperty(property); | |
property = stepDownKey(scope, property); | |
} | |
// If any error occurs during the parsing, return a dummy object | |
// with an error message | |
catch (err) { | |
return { | |
error: err, | |
current: current, | |
object: obj, | |
path: path, | |
scope: scope, | |
property: null, | |
get: function() { /* noop */ }, | |
set: function() { /* noop */ } | |
}; | |
} | |
// Return the "scoped" object property descriptor | |
return { | |
error: null, | |
current: current, | |
object: obj, | |
path: path, | |
scope: scope, | |
property: property, | |
get: function() { | |
return scope[property]; | |
}, | |
set: function(value) { | |
scope[property] = value; | |
} | |
}; | |
} | |
// | |
// Parses a single property step in the path | |
// | |
// @param {prop} the property string (eg. "foo", "[0]", or "{bar:baz}") | |
// @return string|array | |
// | |
function parseProperty(prop) { | |
var wrapped; | |
// "Array" style bracket syntax | |
if (wrapped = wrappedIn(prop, '[', ']')) { | |
return wrapped; | |
} | |
// "Object" style key:value pair syntax | |
else if (wrapped = wrappedIn(prop, '{', '}')) { | |
return wrapped.split(':'); | |
} | |
// "Standard" string property name | |
else { | |
return prop; | |
} | |
} | |
// | |
// Determines if a string is wrapped in the given characters, and returns the unwrapped | |
// string if it is | |
// | |
// @param {str} the string to check | |
// @param {begin} the opening string | |
// @param {end} the closing string | |
// @return string|false | |
// | |
function wrappedIn(str, begin, end) { | |
if (str[0] === begin && str.slice(-1) === end) { | |
return str.slice(1, -1); | |
} | |
return false; | |
} | |
// | |
// Given a parsed property from `parseProperty`, find the actual matching property name | |
// on the real object so we can step down into the next scope | |
// | |
// @param {obj} the object to look in | |
// @param {prop} the property to look for | |
// @return string | |
// | |
function stepDownKey(obj, prop) { | |
if (Array.isArray(prop)) { | |
Object.keys(obj).some(function(key) { | |
var value = obj[key]; | |
if (value && typeof value === 'object' && value[prop[0]] === prop[1]) { | |
prop = key; | |
return true; | |
} | |
}); | |
} | |
return prop; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment