Cloning Objects: Merging and Matching of objects
Creating a new object that merges or copies other objects can create "referential" links that can burn you later on. You can easily have two objects with members that actually refer to the same address in memory. Changing one will change the other! This can cause major bugs if you are not expecting this!
I use these functions mostly when dealing with AJAX calls and "call back" functions that fire when web responses come through. I also use them when "cloning" objects to make "new" objects (ones that might be "saving" filter settings that users can maintain and copy and what not).
Function #1: mergeObjects()
This function will merge two objects. If the "property" already exists it WILL NOT be touched. Any new properties in objFiller will be created in objMaster.
This function copies the "value" of the element, so it should only be used for objects that have "value" type properties only.
If you are merging objects that have properties that are arrays or objects themselves, use the next function (mergeObjectsDeep)
Code:
function mergeObjects(objMaster, objFiller) {
for (var propertyName in objFiller) {
if (typeof objMaster[propertyName] == "undefined") {
objMaster[propertyName] = objFiller[propertyName];
}
}
}
Re: Cloning Objects: Merging and Matching of objects
Function #2: mergeObjectsDeep()
This function goes deeper - seeing "what" is being merged.
If the property is an "array" it gets copied with .slice(0) so that a "new" array gets created in memory.
If the property is an "object" then the mergeObjectsDeep() function is called recursively to move that element.
Otherwise the value is copied.
[edit] Only properties that do not already exist are copied. This is the same logic in mergeObjects() [/edit]
Code:
function mergeObjectsDeep(objMaster, objFiller) {
for (var propertyName in objFiller) {
if (typeof objMaster[propertyName] == "undefined") {
if (Object.prototype.toString.call(objFiller[propertyName]) == "[object Array]") {
objMaster[propertyName] = objFiller[propertyName].slice(0);
} else if (Object.prototype.toString.call(objFiller[propertyName]) == "[object Object]") {
objMaster[propertyName] = {};
mergeObjectsDeep(objMaster[propertyName], objFiller[propertyName]);
} else {
objMaster[propertyName] = objFiller[propertyName];
}
}
}
}
Function #3: mergeObjectsShallow()
This function differs from mergeObjectsDeep in that it will only "create" an empty array or object if the property being copied is an array or object.
Code:
function mergeObjectsShallow(objMaster, objFiller) {
for (var propertyName in objFiller) {
if (typeof objMaster[propertyName] == "undefined") {
if (Object.prototype.toString.call(objFiller[propertyName]) == "[object Array]") {
objMaster[propertyName] = [];
} else if (Object.prototype.toString.call(objFiller[propertyName]) == "[object Object]") {
objMaster[propertyName] = {};
} else {
objMaster[propertyName] = objFiller[propertyName];
}
}
}
}
Function #4: mergeObjectsArray()
This function specifically moves an "array" of "objects". Pushing a new empty object onto the array and then using mergeObjectsDeep to clone the element in that array slot.
Code:
function mergeObjectsArray(objMaster, objFiller) {
for (var i = 0; i < objFiller.length; i++) {
objMaster.push({});
mergeObjectsDeep(objMaster[objMaster.length - 1], objFiller[i]);
}
}
Re: Cloning Objects: Merging and Matching of objects
Function #5: matchObjects()
Here is a function to see if two objects match. This is a simple function and should only be used on objects that contain just value properties (string, numeric, boolean).
Code:
function matchObjects(objMaster, objCheck) {
var blnMatch = true;
for (var propertyName in objCheck) {
if (typeof objMaster[propertyName] == "undefined") {
blnMatch = false;
break;
}
if (!(objMaster[propertyName] == objCheck[propertyName])) {
blnMatch = false;
break;
}
}
if (blnMatch) {
for (var propertyName in objMaster) {
if (typeof objCheck[propertyName] == "undefined") {
blnMatch = false;
break;
}
if (!(objCheck[propertyName] == objMaster[propertyName])) {
blnMatch = false;
break;
}
}
}
return blnMatch;
}
Re: Cloning Objects: Merging and Matching of objects
Example of use: mergeObjects()
Both the objWebParam and options "object" contain an .objReturn property - which is an object.
The .objReturn "object" contains just value properties (string, numeric, boolean). It does NOT contain properties that are array or objects themselves.
Any "property" found in options.objReturn that does not already exist in objWebParam.objReturn will be created with that value copied.
Code:
mergeObjects(objWebParam.objReturn, options.objReturn);
Example of use: matchObjects()
Comparing two .objReturn objects. These objects contain just value properties (string, numeric, boolean) allowing this simple function to work.
Code:
blnMatchSproc = matchObjects(gaSprocs[i].what.options.objReturn, objOptions.objReturn);
Example of use: mergeObjectsDeep(), mergeObjectsShallow() and mergeObjectsArray()
This example is cloning a complex array element into a new slot in that same array. This is an array of objects.
I am retaining the length, pushing a new object on, and then taking the [i] slot of the array and then cloning it's many members.
A more intimate knowledge of the property types of the cloned object are needed so you can navigatee the many ways to move properties (you will notice I use just .slice() in places where a simple array is being moved or intentionally leaving it off to "force" a connection to the original array).
Code:
gmOffset = g_objGMaker.length;
g_objGMaker.push({ status: "clone", clone: i, gm: gmOffset, gname: sName, filterTab: -1, gmProperties: {}, parentInfo: {}, gmSource: {} });
g_objGMaker[gmOffset].gmSource.sourceOrig = g_objGMaker[i].gmSource.sourceOrig;
g_objGMaker[gmOffset].gmSource.sourceOutput = g_objGMaker[i].gmSource.sourceOutput.slice(0);
g_objGMaker[gmOffset].gmSource.sourceOutput.gm = gmOffset;
mergeObjectsDeep(g_objGMaker[gmOffset].parentInfo, g_objGMaker[i].parentInfo);
g_objGMaker[gmOffset].parentInfo.original = false;
mergeObjectsShallow(g_objGMaker[gmOffset].gmProperties, g_objGMaker[i].gmProperties);
mergeObjectsShallow(g_objGMaker[gmOffset].gmProperties.settingsObj, g_objGMaker[i].gmProperties.settingsObj);
mergeObjectsShallow(g_objGMaker[gmOffset].gmProperties.curGraph, g_objGMaker[i].gmProperties.curGraph);
for (var j = 0; j < g_objGMaker[i].gmProperties.arrFilters.length; j++) {
g_objGMaker[gmOffset].gmProperties.arrFilters.push({});
mergeObjectsShallow(g_objGMaker[gmOffset].gmProperties.arrFilters[g_objGMaker[gmOffset].gmProperties.arrFilters.length - 1], g_objGMaker[i].gmProperties.arrFilters[j]);
g_objGMaker[gmOffset].gmProperties.arrFilters[g_objGMaker[gmOffset].gmProperties.arrFilters.length - 1].alphaCountList = g_objGMaker[i].gmProperties.arrFilters[j].alphaCountList.slice(0);
mergeObjects(g_objGMaker[gmOffset].gmProperties.arrFilters[g_objGMaker[gmOffset].gmProperties.arrFilters.length - 1].alphaCountUsage, g_objGMaker[i].gmProperties.arrFilters[j].alphaCountUsage);
g_objGMaker[gmOffset].gmProperties.arrFilters[g_objGMaker[gmOffset].gmProperties.arrFilters.length - 1].dataList = g_objGMaker[i].gmProperties.arrFilters[j].dataList.slice(0);
mergeObjects(g_objGMaker[gmOffset].gmProperties.arrFilters[g_objGMaker[gmOffset].gmProperties.arrFilters.length - 1].dataUsage, g_objGMaker[i].gmProperties.arrFilters[j].dataUsage);
mergeObjectsArray(g_objGMaker[gmOffset].gmProperties.arrFilters[g_objGMaker[gmOffset].gmProperties.arrFilters.length - 1].filter, g_objGMaker[i].gmProperties.arrFilters[j].filter);
}
if (g_objGMaker[gmOffset].gmProperties.settingsObj.colsum) {
mergeObjectsArray(g_objGMaker[gmOffset].gmProperties.settingsObj.colsum, g_objGMaker[i].gmProperties.settingsObj.colsum);
}
if (g_objGMaker[gmOffset].gmProperties.settingsObj.selectedcols) {
mergeObjectsArray(g_objGMaker[gmOffset].gmProperties.settingsObj.selectedcols, g_objGMaker[i].gmProperties.settingsObj.selectedcols);
}
if (g_objGMaker[gmOffset].gmProperties.settingsObj.arrcontrol) {
g_objGMaker[gmOffset].gmProperties.settingsObj.arrcontrol = g_objGMaker[i].gmProperties.settingsObj.arrcontrol.slice(0);
}
if (g_objGMaker[gmOffset].gmProperties.settingsObj.columns) {
g_objGMaker[gmOffset].gmProperties.settingsObj.columns = g_objGMaker[i].gmProperties.settingsObj.columns.slice(0);
}
if (g_objGMaker[gmOffset].gmProperties.messagepump) {
g_objGMaker[gmOffset].gmProperties.messagepump = g_objGMaker[i].gmProperties.messagepump.slice(0);
}