ResourceLoader/Default modules/compareObject test
From MediaWiki.org
|
|
This MediaWiki page is inactive and kept for historical interest. It may document extensions or features that are obsolete and/or no longer supported. Do not rely on the information here being up-to-date. --Krinkle 23:36, 13 December 2010 (UTC) |
In /resources/mediawiki/mediawiki.js a jQuery prototype was added by Krinkle to compare objects for equalness by iterating over the properties and comparing values. most of it is pretty straight forward, except for one thing and that is: How to check if there are no extra properties in ObjectB that do not exist in ObjectA ?
There were two concepts:
- Keep a
A_sizevariable and increment it by 1 throughout the loop of properties of ObjectA
then after the loop do another loop through ObjectB's properties and keep aB_sizevariable and compare the two size-variables afterwards - After the loop of ObjectA's properties do another loop of ObjectB's properties and check if it is in ObjectA, if not return false.
[edit] Script
Following is a script that defines both functions (compareObject_count() and compareObject_forIn) and defines a few more functions to mass-execute test cases. The results are included at the bottom.
/** * The COUNT-method */ compareObject_count = function ( objectA, objectB ) { // Do a simple check if the types match if ( typeof( objectA ) == typeof( objectB ) ) { // Only loop over the contents if it really is an object if ( typeof( objectA ) == 'object' ) { // If they are aliases of the same object (ie. mw and mediaWiki) return now if ( objectA === objectB ) { return true; } else { // Keep track of the size so we can check the number of properties afterwards var A_size = 0; // Iterate over each property for ( var prop in objectA ) { // Check if this property is also present in the other object if ( prop in objectB ) { // Compare the types of the properties var type = typeof( objectA[prop] ); if ( type == typeof( objectB[prop] ) ) { // Recursively check objects inside this one switch ( type ) { case 'object' : if ( !compareObject_count( objectA[prop], objectB[prop] ) ) { return false; } break; case 'function' : if ( objectA[prop].toString() !== objectB[prop].toString() ) { return false; } break; default: if ( objectA[prop] !== objectB[prop] ) { return false; } break; } A_size++; } else { return false; } } else { return false; } } // Calculate B's size var B_size = 0; for ( var prop in objectB ) { B_size++; } if ( A_size != B_size ) { return false; } } } } else { return false; } return true; } function runTest_count(){ // the same var a = compareObject_count( { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 }, { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 } ); // different var b = compareObject_count( { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 }, { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 510 } ); // extra stuff in B var c = compareObject_count( { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 }, { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500, 'extra' : 'property' } ); return a + '/' + b + '/' + c; } /** * The FOR IN-Method */ compareObject_forIn = function ( objectA, objectB ) { // Do a simple check if the types match if ( typeof( objectA ) == typeof( objectB ) ) { // Only loop over the contents if it really is an object if ( typeof( objectA ) == 'object' ) { // If they are aliases of the same object (ie. mw and mediaWiki) return now if ( objectA === objectB ) { return true; } else { // Iterate over each property for ( var prop in objectA ) { // Check if this property is also present in the other object if ( prop in objectB ) { // Compare the types of the properties var type = typeof( objectA[prop] ); if ( type == typeof( objectB[prop] ) ) { // Recursively check objects inside this one switch ( type ) { case 'object' : if ( !compareObject_forIn( objectA[prop], objectB[prop] ) ) { return false; } break; case 'function' : if ( objectA[prop].toString() !== objectB[prop].toString() ) { return false; } break; default: if ( objectA[prop] !== objectB[prop] ) { return false; } break; } } else { return false; } } else { return false; } } // Check for properties in B but not in A // This is about 15% faster (tested in Safari 5 and Firefox 3.6) // ...than incrementing a count variable in the above and below loops // and comparing the numbers afterwards // for ( var prop in objectB ) { if ( !( prop in objectA ) ) { return false; } } } } } else { return false; } return true; } function runTest_forIn(){ // the same var a = compareObject_forIn( { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 }, { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 } ); // different var b = compareObject_forIn( { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 }, { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 510 } ); // extra stuff in B var c = compareObject_forIn( { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500 }, { 'foo' : 'bar', 'string' : 'here', 'func' : function(){ alert('test'); }, 'obj' : { 'foo' : 'string' }, 'num' : 500, 'extra' : 'property' } ); return a + '/' + b + '/' + c; } /** * The test execution */ var numerOfTests = 1000; function runAllTests(){ for ( i=0; i < numerOfTests; i++ ) { runTest_count(); runTest_forIn(); } }
[edit] Results
The following results were achieved like this:
- Open Safari 5 on a Mac
- Go to about:blank
- Open up the Web Inspector to the Console
- Paste the above script
- Go to Profiling and start recording
- Execute
runAllTests()twice - Stop recording
- Results:
| Self | Total | Cals | Function |
|---|---|---|---|
| 0.39% | 1.27% | 2000 | runTest_count |
| 0.17% | 1.07% | 2000 | runTest_forIn |
(1-(1.07/1.27))*100 = 15.7%