Skip to content

Instantly share code, notes, and snippets.

@mmarinero
Last active February 4, 2016 13:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmarinero/3c9d3247e615628c64d9 to your computer and use it in GitHub Desktop.
Save mmarinero/3c9d3247e615628c64d9 to your computer and use it in GitHub Desktop.
Two functions to traverse a chain of objects in Javascript. One to ask for a value at the end and one to place a value and create the intermediate objects if necessary. Both accept a variable length parameter's list or an array.
/**
* Tries to follow a chain of objects starting at base and return the value at
* the end. It uses a string array "mixedChain" to represent the chain and also
* accepts a variable number of string parameters representing the same chain so
* ask(object, ["child1", "child2"]) is equivalent to
* ask(object, "child1", "child2")
* If the chain keys are not strings convert them or use the array syntax
*
* @param {Object} base Root object of the chain to be inspected.
* @param {array[String] | ...String} mixedChain List of strings representing
* the objects properties chain.
* @return {mixed} value of the property at the end of the chain or undefined
* if not found.
*/
var ask = function(base, mixedChain) {
var i = 0;
var chain = mixedChain;
if (Object.prototype.toString.call(chain) !== '[object Array]') {
chain = arguments;
i = 1;
}
while (i < chain.length - 1 && base !== undefined) {
base = base[chain[i]] || undefined;
i++;
}
return base ? base[chain[i]] : undefined;
};
/**
* Builds a chain of objects if they don't exist and places a value at the end.
* It uses a string array "mixedChain" to represent the chain and also
* accepts a variable number of string parameters representing the same chain, or a
* single dot separated string that will be splitted and used as chain
* If the chain keys are not strings convert them or use the array syntax
* The last parameter is always the value to be placed.
* build(object, ["child1", "child2"], value) is equivalent to
* build(object, "child1", "child2", value) and equivalent to
* build(object, "child1.child2", value)
*
* @param {Object} base Root object of the chain to be used.
* @param {array[String] | ...String | String} mixedChain List of strings representing
* the objects properties chain. If it is a dot separated string, the string is splitted
* and used as if it were an array. (Use the array version if your second property names may have dots)
* @param {mixed} mixedVal Value to be placed at the end of the chain, it
* represents the last parameter although, positionally, it's the third. If it is an objects
* it will merge it's contents
*/
var build = function(base, mixedChain, mixedVal) {
var args_index = 0;
var chain = mixedChain;
var val = mixedVal;
var isArray = Object.prototype.toString.call(chain) === '[object Array]';
if (!isArray) {
var splitted = chain.split('.');
if (splitted.length < 2) {
chain = arguments;
//if the chain is the arguments object avoid the base argument
args_index = 1;
} else {
chain = splitted;
}
val = arguments[arguments.length - 1];
}
var i = args_index;
while (i < chain.length - args_index - 1) {
if (base[chain[i]] === undefined) {
base[chain[i]] = {};
}
base = base[chain[i]];
i++;
}
if (typeof val === 'object') {
base[chain[i]] = Object.assign(base[chain[i]] || {}, val);
} else {
base[chain[i]] = val;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment