ko.viewmodel vs ko.mapping - viewmodel creation vs custom model

JavaScript performance comparison

Revision 9 of this test case created by Tamjeed

Preparation code

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.0/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.3.3/knockout.mapping.js"></script>
<script src="http://coderenaissance.github.com/knockout.viewmodel/knockout.viewmodel.min.js"></script>
 <SCRIPT LANGUAGE="JavaScript">
var	order = {
				items:[
						{
							string:"item3",
							number:3,
							anotherObject:{
											items:[{id:4, name:"item4"},{id:5, name:"Item5"}] 
										  }      
						},
						{
							string:"Item0",
							number:0,
							anotherObject: {
											items:[{id:1, name:"Item1"},{id:2, name:"Item2"}] 
										   }      
						}
					  ]
			};
 
var mapper = {
  ".items":{t:"items",map:[{f:"*",t:"*"}]},
  ".items.anotherObject":{t:"items.anotherObject",map:[{f:"*",t:"*"}]},
  ".items.anotherObject.items":{t:"items.anotherObject.items",map:[{f:"*",t:"*"}]},
};

var t = 0;
var parentPathKey = "*";
print = function(parentPath,t,obj,toObj,indexArr){
    if( obj ){
		
		for(var i in obj){

			//var tabs = ""; for(var j=t;--j;){ tabs+="\t";}s+=tabs;s+=i;

			if( obj[i] && i !== parentPathKey ){
//			console.log(parentPath+"."+i);
			var currObject = obj[i];
				// check for mapping
				var mapping = mapper[parentPath+"."+i];

				if( currObject instanceof Array ){
					var constructedObject = toObj;
					if( mapping ){
							 // if t then create or get t, else i becomes t should be an array
							 if( mapping.t ){
								constructedObject = createOrGetObjectArr( mapping.t, toObj, indexArr);
							 }else{
								constructedObject = createOrGetObjectArr( i, toObj , indexArr);
							 }
					}
					var currObjectArrLen = currObject.length
					for(var k = 0; k < currObjectArrLen; k++){
					var currObjectInArr = currObject[k];
						if( !currObjectInArr[parentPathKey] && k !== parentPathKey){
							currObjectInArr[parentPathKey] = obj;
						}
						//s+="\n"+tabs+"[";
						
						
						if( mapping ){
							 
							 var constructedObjectItem = constructedObject[constructedObject.length] = {};
							
							 if(mapping.map){
								 var mappingMapLen = mapping.map.length;
								 // iterate through mappings
								 for(var itr = 0; itr < mappingMapLen;itr++ ){
								 var nestedMapping = mapping.map[itr];
								 // the array could of objects or direct values
								 // m.f = * then copy all the obj to m.t object
									if(  nestedMapping.f === "*" ){
										// clone
										 copyAllproperties(constructedObjectItem, currObjectInArr);//TODO: check if nested paths
									}else{
										 // m.f = index then and set it to map.t//TODO: handle index 
										 //	m.f = has parent, then iterate and set to m.t object
										if( nestedMapping.f ){
											// get value
											if( nestedMapping.t ){
												createAndSet(nestedMapping.t,constructedObjectItem
												,nestedMapping.f,currObjectInArr);
											}
										 }
									}
								 }
							}
						}
						indexArr.push(constructedObject.length - 1);
						if( i !== parentPathKey ){
						
							print(parentPath+"."+i, t+1, currObjectInArr, toObj, indexArr);
							
						}
						indexArr.pop();
						delete currObjectInArr[parentPathKey];
						//s+=tabs+"]";
					}
				}else if( currObject instanceof Object ){
					if( currObject[parentPathKey] && i !== parentPathKey){
						currObject[parentPathKey] = obj;
					}
					//s+="\n"+tabs+"{";
					var constructedObject = toObj;
					if( mapping ){
						 // if t then create or get t, else i becomes t
						 if( mapping.t ){
							contructedObject = createOrGetObject( mapping.t, toObj,false, indexArr );
						 }else{
							contructedObject = createOrGetObject( i, toObj, false, indexArr );
						 }
						 var mappingMapLen = mapping.map.length;
						  // iterate through mappings
						 for(var itr = 0; itr < mappingMapLen;itr++ ){
							var nestedMapping = mapping.map[itr];
							 // the array could of objects or direct values
							 // m.f = * then copy all the obj to m.t object
							if(  nestedMapping.f === "*" ){
								// clone
								copyAllproperties( contructedObject, currObject );//TODO: check if nested paths
							}else{
								 // m.f = index then and set it to map.t//TODO: handle index 
								 //	m.f = has parent, then iterate and set to m.t object
								if( nestedMapping.f ){
									if( nestedMapping.t ){
										createAndSet(nestedMapping.t, contructedObject
										,nestedMapping.f, currObject);
									}
								}
							}
						}
					}
					if( i !== parentPathKey ){
						print( parentPath+"."+i, t+1, currObject, toObj , indexArr);
					}
					delete currObject[parentPathKey];
					//s+=tabs+"}";

				}else{
					if( mapping ){
						if( mapping === "*"){
							toObj[i] = currObject;
						}else{
							if( mapping ){
								// m.f or i, set it to m.t
								if( mapping.f ){
									if( mapping.t ){//TODO: the constructedObject is just a primitive type
										createAndSet(mapping.t,toObj,i,toObj);
									}else{
										createAndSet(i,toObj,i,toObj);
									}
								}
							}
						}
					}
				}
			}
			//s+="\n";
		}
	}
}

function createAndSet(toPath, to, fromPath, from){
	var splits = toPath.split(".");  
	var constructed = to; 
	var len = splits.length;
	
	for( var i = 0; i < len - 1; i++){ 
	    var currSplit = splits[ i ];
		constructed = constructed[ currSplit ] = constructed[ currSplit ] || {}; 
	}
	constructed[ splits[ len - 1 ] ] = getFromValue( fromPath, from );
}
function createOrGetObjectArr( path, o, indexArr){
	return createOrGetObject(path, o, true, indexArr);
}
function createOrGetObject( path, o, isArray, indexArr){
// TODO: incorporate, arrays
	var splits = path.split(".");  
	var constructed = o; 
	var len = splits.length;
	for( var i = 0; i < len; i++){ 
		var currSplit = splits[ i ];
		
		var tmp = constructed[ currSplit ] = constructed[ currSplit ] || ( isArray ? []:{});
		if( tmp instanceof Array && i+1 < len ){
			constructed = tmp[ tmp.length - 1 ];
		}else{
			constructed = tmp;
		}
	}
	return constructed;
}
function copyAllproperties(to, from){
	for(var k in from) {
		if(k !== parentPathKey){
		var currObject = from[k];
			if( !( currObject instanceof Array 
						||currObject instanceof Object ) ){//TODO: re-iterate on this functionality
				to[k] = from[k]
			}
		}
	};
}
function getFromValue(path, obj){
	// check if parent exists, parent should invoke a parent call.
	// TODO: throw an error if the property does not exist
	var splits = path.split(".");
	var len = splits.length;
	if( len > 1 ){
		for( var i = 0; i < len; i++){
		var curSplit = splits[i];
			if(curSplit === "parent"){
				obj = obj[parentPathKey];
			}else{
				if(obj[curSplit]){
					obj = obj[curSplit];
				}
			}
		}
	}
	return obj;
}
</script>
      
<script>
Benchmark.prototype.setup = function() {
  var numberOfArrayRecords = 100,
  viewmodel = null,
  model = {
      items:[]
  };
  
  for(var x = 0; x < numberOfArrayRecords; x++){
      model.items.push({
          string:"Test",
          number:4,
          anotherObject:{
             items:[{id:4, name:"Test"},{id:7, name:"Test2"}] 
          }      
      });   
          
  }

};
</script>

Preparation code output

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.3.3/knockout.mapping.js"></script> <script src="http://coderenaissance.github.com/knockout.viewmodel/knockout.viewmodel.min.js"></script> <SCRIPT LANGUAGE="JavaScript"> var order = { items:[ { string:"item3", number:3, anotherObject:{ items:[{id:4, name:"item4"},{id:5, name:"Item5"}] } }, { string:"Item0", number:0, anotherObject: { items:[{id:1, name:"Item1"},{id:2, name:"Item2"}] } } ] }; var mapper = { ".items":{t:"items",map:[{f:"*",t:"*"}]}, ".items.anotherObject":{t:"items.anotherObject",map:[{f:"*",t:"*"}]}, ".items.anotherObject.items":{t:"items.anotherObject.items",map:[{f:"*",t:"*"}]}, }; var t = 0; var parentPathKey = "*"; print = function(parentPath,t,obj,toObj,indexArr){ if( obj ){ for(var i in obj){ //var tabs = ""; for(var j=t;--j;){ tabs+="\t";}s+=tabs;s+=i; if( obj[i] && i !== parentPathKey ){ // console.log(parentPath+"."+i); var currObject = obj[i]; // check for mapping var mapping = mapper[parentPath+"."+i]; if( currObject instanceof Array ){ var constructedObject = toObj; if( mapping ){ // if t then create or get t, else i becomes t should be an array if( mapping.t ){ constructedObject = createOrGetObjectArr( mapping.t, toObj, indexArr); }else{ constructedObject = createOrGetObjectArr( i, toObj , indexArr); } } var currObjectArrLen = currObject.length for(var k = 0; k < currObjectArrLen; k++){ var currObjectInArr = currObject[k]; if( !currObjectInArr[parentPathKey] && k !== parentPathKey){ currObjectInArr[parentPathKey] = obj; } //s+="\n"+tabs+"["; if( mapping ){ var constructedObjectItem = constructedObject[constructedObject.length] = {}; if(mapping.map){ var mappingMapLen = mapping.map.length; // iterate through mappings for(var itr = 0; itr < mappingMapLen;itr++ ){ var nestedMapping = mapping.map[itr]; // the array could of objects or direct values // m.f = * then copy all the obj to m.t object if( nestedMapping.f === "*" ){ // clone copyAllproperties(constructedObjectItem, currObjectInArr);//TODO: check if nested paths }else{ // m.f = index then and set it to map.t//TODO: handle index // m.f = has parent, then iterate and set to m.t object if( nestedMapping.f ){ // get value if( nestedMapping.t ){ createAndSet(nestedMapping.t,constructedObjectItem ,nestedMapping.f,currObjectInArr); } } } } } } indexArr.push(constructedObject.length - 1); if( i !== parentPathKey ){ print(parentPath+"."+i, t+1, currObjectInArr, toObj, indexArr); } indexArr.pop(); delete currObjectInArr[parentPathKey]; //s+=tabs+"]"; } }else if( currObject instanceof Object ){ if( currObject[parentPathKey] && i !== parentPathKey){ currObject[parentPathKey] = obj; } //s+="\n"+tabs+"{"; var constructedObject = toObj; if( mapping ){ // if t then create or get t, else i becomes t if( mapping.t ){ contructedObject = createOrGetObject( mapping.t, toObj,false, indexArr ); }else{ contructedObject = createOrGetObject( i, toObj, false, indexArr ); } var mappingMapLen = mapping.map.length; // iterate through mappings for(var itr = 0; itr < mappingMapLen;itr++ ){ var nestedMapping = mapping.map[itr]; // the array could of objects or direct values // m.f = * then copy all the obj to m.t object if( nestedMapping.f === "*" ){ // clone copyAllproperties( contructedObject, currObject );//TODO: check if nested paths }else{ // m.f = index then and set it to map.t//TODO: handle index // m.f = has parent, then iterate and set to m.t object if( nestedMapping.f ){ if( nestedMapping.t ){ createAndSet(nestedMapping.t, contructedObject ,nestedMapping.f, currObject); } } } } } if( i !== parentPathKey ){ print( parentPath+"."+i, t+1, currObject, toObj , indexArr); } delete currObject[parentPathKey]; //s+=tabs+"}"; }else{ if( mapping ){ if( mapping === "*"){ toObj[i] = currObject; }else{ if( mapping ){ // m.f or i, set it to m.t if( mapping.f ){ if( mapping.t ){//TODO: the constructedObject is just a primitive type createAndSet(mapping.t,toObj,i,toObj); }else{ createAndSet(i,toObj,i,toObj); } } } } } } } //s+="\n"; } } } function createAndSet(toPath, to, fromPath, from){ var splits = toPath.split("."); var constructed = to; var len = splits.length; for( var i = 0; i < len - 1; i++){ var currSplit = splits[ i ]; constructed = constructed[ currSplit ] = constructed[ currSplit ] || {}; } constructed[ splits[ len - 1 ] ] = getFromValue( fromPath, from ); } function createOrGetObjectArr( path, o, indexArr){ return createOrGetObject(path, o, true, indexArr); } function createOrGetObject( path, o, isArray, indexArr){ // TODO: incorporate, arrays var splits = path.split("."); var constructed = o; var len = splits.length; for( var i = 0; i < len; i++){ var currSplit = splits[ i ]; var tmp = constructed[ currSplit ] = constructed[ currSplit ] || ( isArray ? []:{}); if( tmp instanceof Array && i+1 < len ){ constructed = tmp[ tmp.length - 1 ]; }else{ constructed = tmp; } } return constructed; } function copyAllproperties(to, from){ for(var k in from) { if(k !== parentPathKey){ var currObject = from[k]; if( !( currObject instanceof Array ||currObject instanceof Object ) ){//TODO: re-iterate on this functionality to[k] = from[k] } } }; } function getFromValue(path, obj){ // check if parent exists, parent should invoke a parent call. // TODO: throw an error if the property does not exist var splits = path.split("."); var len = splits.length; if( len > 1 ){ for( var i = 0; i < len; i++){ var curSplit = splits[i]; if(curSplit === "parent"){ obj = obj[parentPathKey]; }else{ if(obj[curSplit]){ obj = obj[curSplit]; } } } } return obj; } </script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
ko.mapping
viewmodel = ko.mapping.fromJS(model);
pending…
ko.viewmodel
viewmodel = ko.viewmodel.fromModel(model);
pending…
custom
for(var i=100;i--;){ print("", 1, order, {},[]);}
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments