UNPKG

dosycrux

Version:

dosy crux - the crux anasta of JavaScript tool belts

137 lines (125 loc) 4.38 kB
"use strict"; { const SHALLOW_EQUALITY_SUFFICES = new Set([ Undefined, Null, String, Number, Boolean ]); const crux = { T, ts, eq, Null, Undefined }; try { module.exports = crux; } catch(e) { Object.assign( self, { crux } ); } //test_eq(); // true if javascript strict equals // either the primitive is the same value or the object is the same memory location function same( a, b ) { return a === b; } // true if semantically equal and same type function eq2( a, b ) { return eq(a,b) && (ts(a) == ts(b)); } // true if semantically equal // false otherwise function eq( a, b, { strict_symbols: strict_symbols = false, decircularize : decircularize = new Set(), ignore_circular : ignore_circular = true } = {}) { if ( decircularize.has( a ) || decircularize.has( b ) ) { throw new TypeError( `Equality of objects with circular references cannot be determined with this version of eq. Please develop a version that can.` ); } else { decircularize.add( a ); decircularize.add( b ); } const ta = T(a); if ( SHALLOW_EQUALITY_SUFFICES.has( ta ) ) { return a == b; } // else check deep const snapshot_a = get_snapshot( a ); const snapshot_b = get_snapshot( b ); const slotnames_a = Object.getOwnPropertyNames( snapshot_a ); const slotnames_b = Object.getOwnPropertyNames( snapshot_b ); if ( slotnames_a.length != slotnames_b.length ) { return false; } slotnames_a.sort(); slotnames_b.sort(); const slotnamestring_a = slotnames_a.join(','); const slotnamestring_b = slotnames_b.join(','); if ( slotnamestring_a != slotnamestring_b ) { return false; } const symbols_a = Object.getOwnPropertySymbols( snapshot_a ); const symbols_b = Object.getOwnPropertySymbols( snapshot_b ); if ( symbols_a.length != symbols_b.length ) { return false; } // Symbols are equal if their descriptions are equal let sym_match = true; if ( strict_symbols ) { const sym_string_a = symbols_a.map( s => s.toString() ).sort().join(','); const sym_string_b = symbols_b.map( s => s.toString() ).sort().join(','); sym_match = sym_match && ( sym_string_a == sym_string_b ); } else { const sym_set_b = new Set( symbols_b ); sym_match = sym_match && symbols_a.every( (s,i) => sym_set_b.has( s ) ); } if ( ! sym_match ) { return false; } const names = slotnames_a.concat( symbols_a ); let equal = true; for( const name of names ) { const a_val = snapshot_a[name]; const b_val = snapshot_b[name]; if ( decircularize.has( a_val ) || decircularize.has( b_val ) ) { if ( ignore_circular ) { console.warn( `Warning: circular reference for name ${name}, at least one of the following has been visited before`, a_val, b_val ); continue; } else { throw new TypeError( `Equality of objects with circular references cannot be determined with this version of eq. Please develop a version that can.` ); } } const val_equal = eq( a_val, b_val, { decircularize } ); equal = equal && val_equal; if ( ! equal ) break; } return equal; } function get_snapshot( o ) { const oshot = Object.create( null ); const names = Object.getOwnPropertyNames( o ).concat( Object.getOwnPropertySymbols( o ) ); for( const name of names ) { oshot[name] = o[name]; } return oshot; } // placeholders for Empty types function Null(){ throw new TypeError(`Null cannot be created.`);} function Undefined(){ throw new TypeError(`Undefined cannot be created.`);} // the Type as a constructor function T( a ) { if ( a === null ) return Null; if ( a === undefined ) return Undefined; const c = a.constructor; const pc = a.__proto__.constructor; const sc = a.name ? a : null; return c || pc || sc || Object; } // the Type as a String function ts( a ) { const cname = a.constructor ? a.constructor.name : null; const tname = typeNameMatcher.exec( Object.prototype.toString.call( a ) )[1]; if ( tname !== cname && !! cname ) return cname; return tname; } function test_eq() { console.log( eq( { d:"CAT",a:1, b:2,c:3 }, {d:"CAT",a:1,b:2,c:3} ) ); } }