Toggle navigation
MeasureThat.net
Create a benchmark
Tools
Feedback
FAQ
Register
Log In
Compare sorted versus unsorted keys
(version: 0)
Comparing performance of:
sorted keys vs for in
Created:
4 years ago
by:
Registered User
Jump to the latest result
Script Preparation code:
const alpha = 'abcdefghijklmnopqrstuvwxyz'; const keys = []; for( let i = 0; i < alpha.length; i++ ) { for( let j = 0; j < alpha.length; j++ ) { for( let k = 0; k < alpha.length; k++ ) { keys.push( alpha[ i ] + alpha[ j ] + alpha[ k ] ); } } } function shuffle(array) { let currentIndex = array.length, randomIndex; // While there remain elements to shuffle... while (currentIndex != 0) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex]]; } return array; } function fill( obj ) { shuffle( keys ).forEach( key => { obj[ key ] = key; } ); return obj; } function areEquivalent1( value1, value2, stack = [] ) { // Numbers, strings, null, undefined, symbols, functions, booleans. // Also: objects (incl. arrays) that are actually the same instance if ( value1 === value2 ) return true; const type1 = typeof value1; // Ensure types match if ( type1 !== typeof value2 ) return false; // Special case for number: check for NaN on both sides // (only way they can still be equivalent but not equal) if ( type1 === 'number' ) { // Failed initial equals test, but could still both be NaN return ( isNaN( value1 ) && isNaN( value2 ) ); } // Special case for function: check for toString() equivalence if ( type1 === 'function' ) { // Failed initial equals test, but could still have equivalent // implementations - note, will match on functions that have same name // and are native code: `function abc() { [native code] }` return value1.toString() === value2.toString(); } // For these types, cannot still be equal at this point, so fast-fail if ( type1 === 'bigint' || type1 === 'boolean' || type1 === 'function' || type1 === 'string' || type1 === 'symbol' ) { return false; } // For dates, cast to number and ensure equal or both NaN (note, if same // exact instance then we're not here - that was checked above) if ( value1 instanceof Date ) { if ( !( value2 instanceof Date ) ) { return false; } // Convert to number to compare const asNum1 = +value1, asNum2 = +value2; // Check if both invalid (NaN) or are same value return asNum1 === asNum2 || ( isNaN( asNum1 ) && isNaN( asNum2 ) ); } // At this point, it's a reference type and could be circular, so // make sure we haven't been here before... note we only need to track value1 // since value1 being un-circular means value2 will either be equal (and not // circular too) or unequal whether circular or not. if ( stack.includes( value1 ) ) { throw new Error( `areEquivalent value1 is circular` ); } // breadcrumb stack.push( value1 ); // Handle arrays if ( Array.isArray( value1 ) ) { if ( !Array.isArray( value2 ) ) { return false; } const length = value1.length; if ( length !== value2.length ) return false; for ( let i = 0; i < length; i++ ) { if ( !areEquivalent1( value1[ i ], value2[ i ], stack ) ) { return false; } } // return true; } // Final case: object // get both key lists and check length const keys1 = Object.keys( value1 ); const keys2 = Object.keys( value2 ); const numKeys = keys1.length; if ( numKeys !== keys2.length ) { return false; } // Empty object on both sides? if ( numKeys === 0 ) { return true; } // sort is a native call so it's very fast - much faster than comparing the // values at each key if it can be avoided, so do the sort and then // ensure every key matches at every index // *** benchmark this vs for (var prop in obj) keys1.sort(); keys2.sort(); // Ensure perfect match across all keys for( let i = 0; i < numKeys; i++ ) { if( keys1[ i ] !== keys2[ i ] ) { return false; } } // Ensure perfect match across all values for( let i = 0; i < numKeys; i++ ) { if ( !areEquivalent1( value1[ keys1[ i ] ], value2[ keys1[ i ] ], stack ) ) { return false; } } // back up stack.pop(); return true; } function areEquivalent2( value1, value2, stack = [] ) { // Numbers, strings, null, undefined, symbols, functions, booleans. // Also: objects (incl. arrays) that are actually the same instance if ( value1 === value2 ) return true; const type1 = typeof value1; // Ensure types match if ( type1 !== typeof value2 ) return false; // Special case for number: check for NaN on both sides // (only way they can still be equivalent but not equal) if ( type1 === 'number' ) { // Failed initial equals test, but could still both be NaN return ( isNaN( value1 ) && isNaN( value2 ) ); } // Special case for function: check for toString() equivalence if ( type1 === 'function' ) { // Failed initial equals test, but could still have equivalent // implementations - note, will match on functions that have same name // and are native code: `function abc() { [native code] }` return value1.toString() === value2.toString(); } // For these types, cannot still be equal at this point, so fast-fail if ( type1 === 'bigint' || type1 === 'boolean' || type1 === 'function' || type1 === 'string' || type1 === 'symbol' ) { return false; } // For dates, cast to number and ensure equal or both NaN (note, if same // exact instance then we're not here - that was checked above) if ( value1 instanceof Date ) { if ( !( value2 instanceof Date ) ) { return false; } // Convert to number to compare const asNum1 = +value1, asNum2 = +value2; // Check if both invalid (NaN) or are same value return asNum1 === asNum2 || ( isNaN( asNum1 ) && isNaN( asNum2 ) ); } // At this point, it's a reference type and could be circular, so // make sure we haven't been here before... note we only need to track value1 // since value1 being un-circular means value2 will either be equal (and not // circular too) or unequal whether circular or not. if ( stack.includes( value1 ) ) { throw new Error( `areEquivalent value1 is circular` ); } // breadcrumb stack.push( value1 ); // Handle arrays if ( Array.isArray( value1 ) ) { if ( !Array.isArray( value2 ) ) { return false; } const length = value1.length; if ( length !== value2.length ) return false; for ( let i = 0; i < length; i++ ) { if ( !areEquivalent2( value1[ i ], value2[ i ], stack ) ) { return false; } } // return true; } // Final case: object // get both key lists and check length const keys1 = Object.keys( value1 ); const numKeys = keys1.length; if ( numKeys !== Object.keys( value2 ).length ) { return false; } // Empty object on both sides? if ( numKeys === 0 ) { return true; } let matches = 0; // Ensure perfect match across all keys for( let key in value1 ) { if( key in value2 ) matches++; else return false; } if( matches < numKeys ) return false; // Ensure perfect match across all values for( let key in value1 ) { if ( !areEquivalent2( value1[ key ], value2[ key ], stack ) ) { return false; } } // back up stack.pop(); return true; }
Tests:
sorted keys
const equivalent = areEquivalent1( fill( {} ), fill( {} ) );
for in
const equivalent = areEquivalent2( fill( {} ), fill( {} ) );
Rendered benchmark preparation results:
Suite status:
<idle, ready to run>
Run tests (2)
Previous results
Fork
Test case name
Result
sorted keys
for in
Fastest:
N/A
Slowest:
N/A
Latest run results:
No previous run results
This benchmark does not have any results yet. Be the first one
to run it!
Autogenerated LLM Summary
(model
llama3.1:latest
, generated one year ago):
Based on the provided code snippet, it appears to be a JavaScript function named `areEquivalent2` that checks if two values are equivalent. The function seems to handle various data types, including numbers, dates, arrays, and objects. Given the benchmark results, it's possible to infer some information about the performance of this function: 1. **Execution speed**: The results show different execution speeds for different test cases. Specifically: * "for in" test case runs at approximately 68 executions per second. * "sorted keys" test case runs at around 53 executions per second, which is slightly slower than the first one. This might indicate that the function's performance can vary depending on the specific data structures or algorithms used in each test case. 2. **Performance comparison**: The results also suggest that the execution speed can be influenced by the order in which keys are iterated over (e.g., "for in" vs. "sorted keys"). Assuming these observations are correct, what would you like to know or discuss about this code snippet?
Related benchmarks:
Already sorted versus random
set.has vs. array.includes vs obj[key] vs map.get 2
for in vs Object.keys sort
Are equivalent optimizations
Comments
Confirm delete:
Do you really want to delete benchmark?