Skip to content

Instantly share code, notes, and snippets.

@cannona
Created April 5, 2013 04:15
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cannona/5316590 to your computer and use it in GitHub Desktop.
Save cannona/5316590 to your computer and use it in GitHub Desktop.
Convert a flat JS object into a nested set of objects and arrays.
var nester = (function () {
/**
* Selected Keys from Object
*
* @param object obj The object from which you want to get the keys.
* @param array ignoredKeys keys to ignore.
* @return array the non-ignored keys of the object.
*/
var selectedKeysFromObject = function (obj, ignoredKeys) {
var ignoredKeysObj = {};
for (var i = 0; i < ignoredKeys.length; i++) {
ignoredKeysObj[ignoredKeys[i]] = true;
}
var keys = [];
for (key in obj) {
if (ignoredKeysObj[key] || !obj.hasOwnProperty(key)) {
continue;
}
keys.push(key);
}
return keys;
};
/**
* Populate Object
*
* A recursive function that adds to a nested object from a flat array and a list of properties.
*
* @param array source The source array that we wish to nest inside the object.
* @param Object destination The object to nest the data inside.
* @param array properties A list of properties to be nested, ordered from outer to inner.
* @param propertiesIndex internal use, should not be specified by outside calling functions.
* @return void
*/
var populateObj = function populateObjFunc(source, destination, properties, propertiesIndex) {
// the current property we are nesting.
if (!propertiesIndex) {
propertiesIndex = 0;
}
var property = properties[propertiesIndex];
propertiesIndex++;
// The object matching the current property, and the one being nested.
var nestedObj;
// Has this property been nested inside this object before?
if (!destination[property]) {
destination[property] = []
} else {
// We found a list, check to see if we can find the nested object matching the property value.
for (var i = 0; i < destination[property].length; i++) {
if (destination[property][i]['_' + property] === source[property]) {
nestedObj = destination[property][i];
break;
}
}
}
// Did we find the nested object? If not, create it.
if (!nestedObj) {
nestedObj = {};
nestedObj['_' + property] = source[property];
destination[property].push(nestedObj);
}
// if there are more properties to nest, recurse. Otherwise, we've reached the base case.
if (properties.length > propertiesIndex) {
return populateObjFunc(source, nestedObj, properties, propertiesIndex);
} else {
// Add the remaining items not in the properties list to the deepest child object.
var extraKeys = selectedKeysFromObject(source, properties);
for (var i = 0; i < extraKeys.length; i++) {
nestedObj[extraKeys[i]] = source[extraKeys[i]];
}
return;
}
};
/**
* Anonymous function returned by this function.
*
* Loops through all the source objects, and nests them.
*
* @param array records The array of records to be nested.
* @param string propertiesString The -> delimited list of properties to nest.
*/
return function (records, properties) {
var properties = properties.split('->');
var nestedObj = {};
for (var i = 0; i < records.length; i++) {
populateObj(records[i], nestedObj, properties);
}
return nestedObj;
};
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment