ko.viewmodel vs ko.mapping - viewmodel creation vs custom model (v9)

Revision 9 of this benchmark created by Tamjeed on


Description

Performance comparison between the knockout mapping plugins ko.viewmodel and ko.mapping.

With custom mapper

Preparation HTML

<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>

Setup

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"}] 
            }      
        });   
            
    }

Test runner

Ready to run.

Testing in
TestOps/sec
ko.mapping
viewmodel = ko.mapping.fromJS(model);
ready
ko.viewmodel
viewmodel = ko.viewmodel.fromModel(model);
ready
custom
for(var i=100;i--;){ print("", 1, order, {},[]);}
ready

Revisions

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