Last active
February 14, 2023 14:48
-
-
Save DarrenSem/97a436f11dfb841f25837fd449f20756 to your computer and use it in GitHub Desktop.
stringify.js (handles virtually all types unlike the normal JSON.stringify -- intended for inspecting/displaying object values, NOT for serializing/exporting)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// stringify.js (handles virtually all types unlike the normal JSON.stringify -- intended for inspecting/displaying object values, NOT for serializing/exporting) | |
/* | |
this function's result is far more flexible, human-friendly, and USEFUL than JSON.stringify, because... | |
( Infinity ) => 'Infinity' and ( NaN ) => 'NaN' , instead of 'null' | |
( undefined ) => 'undefined' and ( Date ) => 'function Date() { [native code] }' , instead of undefined | |
( new TypeError("xyz") ) => 'TypeError: xyz' , instead of '{}' | |
( /z/img ) => '/z/gim' , instead of '{}' ( JSCRIPT => '/z/igm' ) | |
( [ , ] ) => '[undefined]' , instead of '[null]' | |
( new Array(3) ) => '[undefined,undefined,undefined]' , instead of '[null,null,null]' | |
( new Map( [ ["3", 6.0], [9.0, "8.0"] ] ) ) => 'Map {3 => 6,9 => "8.0"}' , instead of '{}' | |
( new Set( [3, 6.0, "9", Symbol(3)] ) ) => 'Set {3,6,"9",Symbol(3)}' , instead of '{}' | |
( new Set( [ [3, 6] , [9, 12] ] ) ) => 'Set {[3,6],[9,12]}' , instead of '{}' | |
( new WeakSet( [Symbol(3)] ) ) => '[object WeakSet]' , instead of '{}' | |
( new Int8Array(3) ) => '[0,0,0]' , instead of '{"0":0,"1":0,"2":0}' | |
( new Int8Array( [ 3, 6, 9 ] ) ) => '[3,6,9]' , instead of '{"0":3,"1":6,"2":9}' | |
( Symbol(3) ) => 'Symbol(3)' , instead of undefined | |
( x=>Symbol( x ) ) => 'x=>Symbol( x )' , instead of undefined | |
( BigInt(3) ) => 3 , instead of TypeError: Do not know how to serialize a BigInt | |
*/ | |
// with Set/Map "for of" logic, now 401 chars _=function(a){var b,c=String(a),d={}.toString.call(a).slice(8,-1),e=/Array$/.test(d),f="Object"===d,g=0;if(a&&"object"==typeof a){if(c=[],e)for(;g<a.length;)c.push(_(a[g++]));else{if(a.entries)for(g of a.entries(0))c.push(("Map"===d?g[0]+" => ":"")+_(g[1]));if(!c.length)for(g in a)c.push(g+":"+_(a[g]))}b=c.length||f,c=e?"["+c+"]":b?(f?"":d+" ")+"{"+c+"}":a+""}return"string"==typeof a?"\""+a+"\"":c} | |
// JSCRIPT or without Set/Map "for of" logic, just 310 chars _=function(a){var b,c=String(a),d={}.toString.call(a).slice(8,-1),e=/Array$/.test(d),f="Object"===d,g=0;if(a&&"object"==typeof a){if(c=[],e)for(;g<a.length;)c.push(_(a[g++]));else for(g in a)c.push(g+":"+_(a[g]));b=c.length||f,c=e?"["+c+"]":b?(f?"":d+" ")+"{"+c+"}":a+""}return"string"==typeof a?"\""+a+"\"":c} | |
var stringify = function(v) { | |
// "Returns a string representation of the object. | |
// For simple objects toString() will be used, | |
// for complex objects a JSON representation will be given." ( their version anyways: https://edabit.com/help/creating-challenges#javascript ) | |
var elements = String(v); // minifier BREAKS THIS for v = Symbol; must be manually corrected back to c=String(a) because c=a+"" FAILS -- TypeError: Cannot convert a Symbol value to a string | |
var type = {}.toString.call(v).slice(8,-1); | |
var isArray = /Array$/.test(type); | |
var isPlainObject = type === "Object"; | |
var isNonEmpty; | |
var key = 0; | |
if(v && "object" === typeof v) { | |
elements = []; | |
if(isArray) { | |
while(key < v.length) { | |
elements.push( | |
stringify( v[key ++] ) | |
); | |
}; | |
} else { | |
/// >> comment out this "for of" section for JScript... OR if you never call with ES6 "[object Map|Set]" | |
if(v.entries)for(key of v.entries(0)) { | |
elements.push( | |
( type !== "Map" ? "" : ( key[0] + " => " ) ) + | |
stringify( key[1] ) | |
); | |
}; if(!elements.length) | |
for(key in v) { | |
elements.push( | |
key + ":" + stringify( v[key] ) | |
); | |
}; | |
}; | |
isNonEmpty = elements.length || isPlainObject; | |
elements = isArray | |
? ( "[" + elements + "]" ) // no reason for this other than "space looks better": if(isNonEmpty)elements = elements.join(", "); | |
: isNonEmpty ? ( | |
( isPlainObject ? "" : ( type + " " ) ) | |
+ "{" + elements + "}" | |
) : String(v); | |
}; | |
return ( | |
typeof v === "string" ? ( '"' + v + '"' ) // stringify("A\"\'B") === stringify('A\"\'B') -- simpler than... | |
// typeof v === "string" ? /'/.test(v) ? ( '"' + v + '"' ) : ( "'" + v + "'" ) // ...this is probably overkill | |
: elements | |
); | |
}; | |
// debugger; console.log(":" + stringify( new Set([3, 6.0, "9", Symbol(3)]) ) + ":" ); throw "STOP"; // :Set {3,6,"9",Symbol(3)}: , instead of :{}: via JSON.stringify | |
// debugger; console.log(":" + stringify( new Map( [ ["3", 6.0], [9.0, "8.0"] ] ) ) + ":" ); throw "STOP"; // :Map {3 => 6,9 => "8.0"}: , instead of :{}: via JSON.stringify | |
// debugger; console.log(":" + stringify( new WeakSet([Symbol(3), Symbol(6)]) ) + ":" ); throw "STOP"; // :[object WeakSet]: , instead of :{}: via JSON.stringify | |
/*@cc_on @if(@_jscript) console={log:function(){var b=arguments;b.length&&WScript.Echo([].join.call(b," "))}}; location={href:WScript.ScriptFullName}; @end@*/ | |
// ^ JScript needs these additional 237 char, to add these 2 Objects that exist in web browsers... | |
// console={log:function(){var a=arguments;a.length&&WScript.Echo([].join.call(a," "))}}; | |
// location={href:WScript.ScriptFullName}; | |
var stringify_compared_to_JSON = function(func) { | |
return [ | |
func( null ), // => 'null' | |
func( true ), // => 'true' | |
func( false ), // => 'false' | |
func( 0.0 ), // => '0' | |
func( "0.0" ), // => "'0.0'" | |
func( [] ), // => '[]' | |
func( {} ), // => '{}' | |
func( location ), // => .match(/^(Location )?{.*\bhref:\s*".+}$/) != null , .match(/^{ancestorOrigins: '\[object DOMStringList\]'/) != null | |
func( Infinity ), // => 'Infinity' , instead of 'null' | |
func( NaN ), // => 'NaN' , instead of 'null' | |
func( undefined ), // => 'undefined' , instead of undefined | |
func(), // => 'undefined' , instead of undefined | |
func( Date ), // => 'function Date() { [native code] }' , instead of undefined AKA .match(/^\s*function Date\(\) {\s+\[native code\]\s+}\s*$/) != null, | |
func( new TypeError("xyz") ), // => 'TypeError: xyz' , instead of '{}' AKA .match(/^({name: ")?TypeError.+ '?xyz('})?$/) != null | |
func( /z/img ), // => '/z/gim' , instead of '{}' ( JSCRIPT => '/z/igm' ) | |
func( [ , ] ), // => '[undefined]' , instead of '[null]' ( JSCRIPT => '[undefined,undefined]' ) | |
func( new Array(3) ) // => '[undefined,undefined,undefined]' , instead of '[null,null,null]' | |
/// >> comment out this section for JScript... | |
, func( new Map( [ ["3", 6.0], [9.0, "8.0"] ] ) ), // => 'Map {3 => 6,9 => "8.0"}' , instead of '{}' | |
func( new Set( [3, 6.0, "9", Symbol(3)] ) ), // => 'Set {3,6,"9",Symbol(3)}' , instead of '{}' | |
func( new Set( [ [3, 6] , [9, 12] ] ) ), // => 'Set {[3,6],[9,12]}' , instead of '{}' | |
func( new WeakSet( [Symbol(3)] ) ), // => '[object WeakSet]' , instead of '{}' | |
func( new Int8Array(3) ), // => '[0, 0, 0]' , instead of '{"0":0,"1":0,"2":0}' | |
func( new Int8Array( [ 3, 6, 9 ] ) ), // => '[3, 6, 9]' , instead of '{"0":3,"1":6,"2":9}' | |
func( Symbol(3) ), // => 'Symbol(3)' , instead of undefined | |
func( x=>Symbol( x ) ), // => 'x=>Symbol( x )' , instead of undefined | |
func === stringify ? func( BigInt(3) ) : "BigInt TypeError" // => '3' , instead of TypeError: Do not know how to serialize a BigInt | |
]; | |
}; | |
// null | |
// true | |
// false | |
// 0 | |
// "0.0" | |
// [] | |
// {} | |
// +THEN+ ... Location {ancestorOrigins:DOMStringList {length:0,contains:function contains() { [native code] },item:function item() { [native code] }},href:"file:///C:/data/simple.html",origin:"file://",protocol:"file:",host:"",hostname:"",port:"",pathname:"/C:/data/simple.html",search:"",hash:"",assign:function assign() { [native code] },reload:function reload() { [native code] },replace:function replace() { [native code] },toString:function toString() { [native code] }} | |
// Infinity | |
// NaN | |
// undefined | |
// undefined | |
// function Date() { [native code] } | |
// TypeError: xyz | |
// /z/gim | |
// [undefined] | |
// [undefined,undefined,undefined] | |
// Map {3 => 6,9 => 12} | |
// Set {3,6,9} | |
// Set {[3,6],[9,12]} | |
// [object WeakSet] | |
// [0,0,0] | |
// [3,6,9] | |
// Symbol(3) | |
// x=>Symbol( x ) | |
// 3 | |
// +VERSUS+ ... {"ancestorOrigins":{},"href":"file:///C:/data/simple.html","origin":"file://","protocol":"file:","host":"","hostname":"","port":"","pathname":"/C:/data/simple.html","search":"","hash":""} | |
// null | |
// null | |
// <undefined> | |
// <undefined> | |
// <undefined> | |
// {} | |
// {} | |
// [null] | |
// [null,null,null] | |
// {} | |
// {} | |
// [null] | |
// [null,null,null] | |
// {} | |
// {} | |
// {} | |
// {} | |
// {"0":0,"1":0,"2":0} | |
// {"0":3,"1":6,"2":9} | |
// <undefined> | |
// <undefined> | |
// BigInt TypeError | |
console.log( | |
stringify_compared_to_JSON(stringify), | |
Date.now && stringify_compared_to_JSON(JSON.stringify) | |
); | |
console.log([ | |
stringify( null ) === 'null', | |
stringify( true ) === 'true', | |
stringify( false ) === 'false', | |
stringify( 0.0 ) === '0', | |
stringify( "0.0" ) === '"0.0"', | |
stringify( [] ) === '[]', | |
stringify( {} ) === '{}', | |
stringify( location ).match(/^(Location )?{.*\bhref:\s*".+}$/) != null, | |
stringify( Infinity ) === 'Infinity', | |
stringify( NaN ) === 'NaN', | |
stringify( undefined ) === 'undefined', | |
stringify() === 'undefined', | |
stringify( Date ).match(/^\s*function Date\(\) {\s+\[native code\]\s+}\s*$/) != null, | |
stringify( new TypeError("xyz") ).match(/^(Error )?({name:\s*")?TypeError.+"?xyz("})?$/) != null, | |
stringify( /z/img ) === ( Date.now ? '/z/gim' : '/z/igm' ), | |
stringify( [ , ] ).match(/^\[(undefined)((,)\1)?\]$/) != null, // === ( Date.now ? '[undefined]' : '[undefined,undefined]' ), | |
stringify( new Array(3) ).match(/^\[(undefined)(, ?)\1\2\1\]$/) != null | |
/// >> comment out this section for JScript... | |
, stringify( new Map( [ ["3", 6.0], [9.0, "8.0"] ] ) ).match(/^Map {3 => 6,9 => "8.0"}|\[object Map\]/) != null, | |
stringify( new Set( [3, 6.0, "9", Symbol(3)] ) ).match(/^(Set {3,6,"9",Symbol\(3\)}|\[object Set\])$/) != null, | |
stringify( new Set( [ [3, 6] , [9, 12] ] ) ).match(/^Set {\[3,6\],\[9,12\]}|\[object Set\]/) != null, | |
stringify( new WeakSet( [Symbol(3)] ) ) === '[object WeakSet]', | |
stringify( new Int8Array(3) ) === '[0,0,0]', | |
stringify( new Int8Array( [ 3, 6, 9 ] ) ) === '[3,6,9]', | |
stringify( Symbol(3) ) === 'Symbol(3)', | |
stringify( x=>Symbol( x ) ) === 'x=>Symbol( x )', | |
stringify( BigInt(3) ) === '3' | |
]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment