Resursively fold a collection of JavaScript hashes into a singular instance with all possible properties of any instance.
"use strict";
(function () {
var fixture = {
"friends" : [
{
"first" : "Jamshid",
"last" : "Mavandi",
"accounts" : {
"gmail" : "mvndaai",
"facebook" : "mvndaai"
},
"interests" : {
"tv shows" : [
"Julian Smith"
]
}
},
{
"first" : "Brianna",
"middle" : "Marie",
"last" : "Oviatt",
"accounts" : {
"cakewrecks" : "twilight",
"vark" : "twilight"
},
"interests" : {
"music" : [
"Death Cab for Bootie",
"Sunday Night fever"
]
}
},
{
"first" : "AJ",
"last" : "ONeal",
"accounts" : {
"twitter" : "#coolaj86",
"email" : "coolaj86",
"aim" : "coolaj1986",
"linkedin" : "coolaj86",
"phone" : "317-426-5555",
"facebook" : "coolaj86"
},
"interests" : {
"movies" : [
"Star Wars Trilogy",
"Spiderman",
"The princess bride"
]
}
}
]
};
function typeOf(value) {
var s = typeof value;
if (s === 'object') {
if (value) {
if (typeof value.length === 'number' &&
!(value.propertyIsEnumerable('length')) &&
typeof value.splice === 'function') {
s = 'array';
}
} else {
s = 'null';
}
}
return s;
} // Thanks to Crockford
/**
* This assumes a 'sane' data structure.
* * No mixing of set types in arrays - strings XOR (similar) objects
* * No nested arrays - objects make this a mute point. You can always easily describe an array
* * Only like objects in arrays (books, movies, music - not books, toothbrush, tyranasaur)
* * Identical keys of similar objects are assumed to be of the same type
*
* There is no reason that arrays should be nested in javascript
* Nor is there any reason to create a bag of unrelated objects.
*
* It does make sense that if a collection holds similar items
* such as books, movies, and music, that all elements are merged
* for the sake of the template
*
* Since it also makes sense that many elements are undefined on
* any particular instance, this will not attempt to duck-type
* the contents of your collection.
*
*/
function merge_json(fixture, other) {
// figure out what to do with this fixture and other
if ('undefined' === typeOf(fixture)) {
return other; // Gotcha: assigning 'undefined' to an object deletes the key!
} else if ('string' === typeOf(fixture)) {
return string2single(fixture, other);
} else if ('array' === typeOf(fixture)) {
return array2single(fixture, other);
} else if ('object' === typeOf(fixture)) {
return object2single(fixture, other);
} else if ('function' === typeOf(fixture)) {
alert("functions are not supported");
return;
} else {
alert("some weird crap happened: " + typeOf(fixture));
return;
}
// strings are easy - this is where we just throw away values
function string2single(s1, s2) {
return s1 || s2;
}
// put each item in each array through the merge
// then merge all items into one array
function array2single(a1, a2) {
var a = [];
a1 = a1 || [];
a2 = a2 || [];
if ('array' !== typeOf(a1) || 'array' !== typeOf(a2)) {
alert('craptastic! expected array');
}
while (0 !== a1.length) {
a.push(merge_json(a1.pop()));
}
while (0 !== a2.length) {
a.push(merge_json(a2.pop()));
}
while (a.length > 1) {
a1 = a.pop();
a2 = a.pop();
a.push(merge_json(a1, a2));
}
return a;
}
// put each member through the merge
// merge both objects
function object2single(o1, o2) {
var o = {};
// Make sure we can call keys
o1 = o1 || {};
o2 = o2 || {};
if ('object' !== typeOf(o1) || 'object' !== typeOf(o2)) {
alert('craptastic! expected object');
}
// Join the keys of both objects into one array, then merge
union_str_arr(Object.keys(o1), Object.keys(o2)).forEach(function (key, i, arr) {
o1[key] = merge_json(o1[key]);
o2[key] = merge_json(o2[key]);
o[key] = merge_json(o1[key], o2[key]); // || o1[key] || o2[key];
//alert("test: " + JSON.stringify(o[key]));
});
return o;
}
// union two string arrays into 1
function union_str_arr(ka1, ka2) {
var o = {};
if ('undefined' === typeOf(ka1) || 'undefined' === typeOf(ka2)) {
return ka1 || ka2;
}
ka1.forEach(function (key, i, arr) {
o[key] = "";
});
ka2.forEach(function (key, i, arr) {
o[key] = "";
});
return Object.keys(o);
}
}
var fixture1 = "simple string";
var fixture2 = ["array", "of", "strings"];
var fixture3 = {"a": "object", "b": "of", "c": "strings"};
var fixture4 = [{"a": "array", "b": "of", "c": "objects"},{"c": "of", "d": "strings"}];
var fixture5 = {"a": ["object", "of"], "b": ["arrays", "of", "strings"]};
//setTimeout(function () {
var pre = document.createElement('pre');
pre.appendChild(document.createTextNode(JSON.stringify(merge_json(fixture)),null,'\t'));
document.body.insertBefore(pre, document.body.firstChild);
//},10000);
}());