Skip to content

Instantly share code, notes, and snippets.

@bryc
Last active February 27, 2024 09:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryc/03e329df3aa57b529c5b to your computer and use it in GitHub Desktop.
Save bryc/03e329df3aa57b529c5b to your computer and use it in GitHub Desktop.
Various useful js JavaScript snippets I use often - Credit = bryc.github.io
// #### ARRAY <-> STRING CONVERSION ####
// b62 - reversible base62 encoder/decoder in two lines.
function b62(n,c="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"){var r="",l=c.length,i=0;
if(n.trim)while(i<n.length)r=l*r+c.indexOf(n[i++]);else while(n>0)r=c[n%l]+r,n=Math.floor(n/l);return r}
// arst is a reversible string<>array utility in one line. It converts a string to an array of integers and back.
// ES6 (shorter)
arst=a=>{return a.trim?[...a].map(a=>a.charCodeAt()):String.fromCharCode.apply(0,a)}
// ES5 (IE11 compatibility)
function arst(a){return a.trim?Array.apply(0,Array(a.length)).map(function(x,i){return a[i].charCodeAt()}):String.fromCharCode.apply(0,a)}
// arrstr is a reversible string<>array utility in one line. It additionally supports start and end offset.
// also works in IE.
function arrstr(a,s,e){return a.trim?(a=a.slice(s||0,e||a.length))&&Array.apply(0,Array(a.length)).map(function(x,i){return a[i].charCodeAt()}):String.fromCharCode.apply(0,a[a instanceof Array?"slice":"subarray"](s||0,e||a.length))}
// #### INIT ARRAY ####
// init array with incremental bytes (supporting range)
Array(32 + 1).fill().map((e,i)=>(i + 0))
// init array with random bytes
new Uint8Array(32).map(()=>Math.floor(Math.random()*256))
// #### DISPLAY BYTES AS HEX ####
// ES6 version
arrhex=a=>{return[].map.call(a,x=>x.toString(16).padStart(2,0)).join(" ").toUpperCase()}
// IE11 compatibility version
function arrhex(arr){for(var i=0,s="";i<arr.length;i++)s+=("0"+(arr[i]&0xFF).toString(16)).slice(-2);return s;}
// #### PADDING FUNTIONS #### ...gotta clean this up
pad=(i,x=2)=>{return i.toString(16).padStart(x,0).toUpperCase()}
pad=(i,x=3,y=0)=>{return`${i}`.padStart(x,y)}
function pad(a,b,c){return(new Array(b||2).join(c||0)+a).slice(-(b||2))}
function pad(s,w=3,z=' '){return(""+s).padStart(w,z)}
function padhex(s,w=8,z=0){return s.toString(16).toUpperCase().padStart(w,z)}
var padleft = (s,c,len) => { while(s.length < len) s = c + s; return s; }
function pad(pad, str, padLeft) {
if (typeof str === 'undefined')
return pad;
if (padLeft) {
return (pad + str).slice(-pad.length);
} else {
return (str + pad).substring(0, pad.length);
}
}
var leftPad = (s, c, n) => c.repeat(n - s.length) + s;
function l(p,t,v){v+="";return v.length>=t?v:l(p,t,p+v)}
function pad(a,b){return((""+a).length<b)?pad("0"+a,b):a;}
// Old ES6 pad function - Instead of constantly rewriting a pad function, use this:
function pad(n, width=2, z=0){return(String(z).repeat(width)+String(n)).slice(String(n).length)}
// old version of arrstr(): array of bytes to a string, using offsets
function arrstr(arr, start, end) {
arr = arr || [];
start = start || 0;
end = end || arr.length;
var m = arr.constructor === Array ? 'slice' : 'subarray';
arr = arr[m](start, end);
return String.fromCharCode.apply(null, arr);
}
// possibly a very old version?
var arrstr = function(arr, start, end) {
arr = arr || [];
start = start || 0;
end = end || arr.length;
for(var str = "", i = start; i < end; i++) {
var p = arr[i];
str += String.fromCharCode(p);
}
return str;
};
// old and obsolete: string to array of bytes
function strarr(str) {
for(var i=0,arr=[];i<str.length;i++)arr[i]=str.charCodeAt(i)&0xFF;
return arr;
}
// use this to test distribution of function output (such as random)
for(var s = {}, i = 0; i < 99999; i++) {
var t = 0|Math.random()*256;
!s[t] && (s[t] = 0), s[t]++;
} console.log(s);
// String of HEX bytes to array of bytes
function hexToBytes(hex) {
hex = hex.replace(/ /g,'');
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// bi-directional 32-bit integer to byte array (big-endian)
intbyt=(a=>a.map?a[0]<<24|a[1]<<16|a[2]<<8|a[3]:[a>>>24&255,a>>>16&255,a>>>8&255,a&255]);
// bi-directional 32-bit integer to byte array (little-endian)
intbyt=(a=>a.map?a[3]<<24|a[2]<<16|a[1]<<8|a[0]:[a>>>24&255,a>>>16&255,a>>>8&255,a&255]);
/* binary remap (bmap)
This is a binary-to-text encoder/decoder. It is for displaying binary data as text in as few characters as possible.
It works by remapping byte values with unreliable printed characters to offset 256 and onward (currently 74 of them, or 29%).
The idea is simple: "Replace non-printable characters with printable ones."
Binary data can already be represented as text. ASCII characters are defined by binary numbers after all.
However, there are control characters and white space characters which do not have any symbol attached.
My goal was to produce a string of text which represented some binary data, similar to base64. An important requirement
is that the string was in as few characters as possible.
To accomplish this, certain problematic characters which couldn't be copied/pasted had to be remapped.
First 0x00, the null byte. The presence of a null-byte will destroy the string's data integrity as it cannot be copied.
Second, 0x0A, 0x0D - Linefeed and Carriage return. These are "new line" characters which can be troublesome and often corrupt output.
Third, 0xA0: non-break space. This has been a problematic one, as well.
Other non-printable characters had varying levels of success, but ultimately, I had to remap a few ranges of ISO-8859-1:
0x00-0x20 (control characters and space character)
0x7F-0xA0 (DEL key to NBSP)
0xAD ("soft hyphen", invisible? hard to see)
0xB8 ("spacing cedilla", very small/hard to see)
0x3E (the > symbol. will cause problems in HTML)
0x3C (the < symbol. problems in HTML)
0x27 (the ' symbol. quotes can terminate strings)
0x36 (the " symbol. quotes can terminate strings)
0x22 (the & symbol. problems in HTML. invokes html entitie like &nbsp etc.)
With all 74 of these characters remapped, we have a UTF-8 safe string representing binary data.
While one character maps to one byte, many UTF-8 characters are encoded as two bytes per character, putting its
efficiency into question for some use cases. Base91 and even Base64 may be more efficient memory-wise, so this
method is only advised for smaller amounts of binary data that can be copied/pasted and sent over the
web in UTF-8, such as passwords in games.
I think this is a good basis for a more advanced UTF-8 encoding scheme. It may be possible to represent more than
256 binary values in a single UTF-8 character, resulting in a more memory-efficient (and shorter) string. But this would require
probably about 350+ lines of 'real' code for the algorithm to do this.
If memory efficiency is a concern, consider BasE91 or Z85.
*/
function bmap(val, mode = 0) {
var i, data = mode ? [...val].map(a=>a.charCodeAt()) : val.slice(),
bad = [
0x22, 0x26, 0x27, 0x3C, 0x3E, 0xB8, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xAD
];
for(i = 0; i < val.length; i++) {
var idx = bad.indexOf(data[i]);
if(mode ? data[i]>255 : idx>-1) data[i] = mode ? bad[data[i]-0x100] : 0x100+idx;
}
return mode ? data : String.fromCharCode.apply(0,data);
}
/*
Once upon a time, there was this:
var elem = document.createElement("input");
elem.type = "checkbox";
elem.onclick = test;
Needing to generate HTML throug JS, I was looking for a betterway than
above, and elem() is my simple solution for that.
Examples:
1. Document Fragment (useful for appending elements)
elem([])
2. Simple element with innerText
elem(["div","Test"])
3. Appending child element
elem(["ol"],elem(["li","Hi"]))
4. Assign properties to HTMLElement object
elem(["div",{className:"test"}])
TODO: prevent elem("ab") from creating weird invalid <a> element
what about support for style.borderColor etc? or custom html attribute names?
or methods? any better shortcuts for most used stuff or some syntatic sugar?
*/
function elem(options) {
var el = document.createDocumentFragment();
var tag = options[0];
var prop = options[1];
if(typeof tag === "string") {
el = document.createElement(tag);
}
if(typeof prop === "object") {
for (var item in prop) {
el[item] = prop[item];
}
} else if(prop) {
el.innerHTML = prop;
}
for(var i = 1; i < arguments.length; i++) {
if(arguments[i].nodeType > 0) {
el.appendChild(arguments[i]);
}
}
return el;
}
/*
Handy way to replace a target element with another
*/
function replace(target, replacement) {
while(target.firstChild) {
target.removeChild(target.firstChild);
}
target.appendChild(replacement);
}
/* Event handler forwarder
------------------------------
Allows you to include a variable number of arguments in event handlers.
For example: elements[i].onclick = evarg(doThis, i);
For example: elements[i].onclick = evarg(doThis, i, {"name": usr, "location": loc});
Assuming it is done in a loop, this assigns an onclick event to each element.
Instead of simply assigning "doThis" as the onclick function, we specify
another argument i, which also assigns the value of the loop counter in sequence.
In the second argument, we specified another argument cosisting of an object.
function doThis(event, id, info) {
// id will be set to whatever i was when it the event handler was created.
console.log(id);
console.log(info.name, info.location);
}
*/
function evarg(func) {
var args = Array.prototype.slice.call(arguments, 1);
return function(event) {
func.apply(null, args.concat([event]));
};
}
/*
Extend the Function prototype.
Example: elements[i].onclick = doThis.evarg(1);
*/
Function.prototype.evarg = function() {
var func = this, args = Array.prototype.slice.call(arguments);
return function(event) {
func.apply(null, args.concat([event]));
};
};
// Output strings to txt file in JS console. (Used in Chrome)
// Useful for saving console.log outputs.
// Notes: 1) Uses UTF-8 byte order mark. 2) Old code stopped working: new Event('click')
function txtout(str, file="out.txt") {
var A=document.createElement("a");
A.download=file,A.href="data:text/plain;charset=utf-8,%EF%BB%BF"+encodeURIComponent(str),
A.dispatchEvent(new MouseEvent("click"));
}
@bryc
Copy link
Author

bryc commented Mar 31, 2023

When you need to simply check if multiple elements are equal (or any other condition), this is probably the optimal way:

// Returns true if all elements are equal
[1,1,1,1].every((v, i, a) => v === a[0])

// Returns true when an array element is not equal, so inverting Boolean may be desired
// Technically this terminates sooner if a mismatch is found
[1,1,1,1].some((v, i, a) => v !== a[0])

Practical example. The following should return false if the given 8-byte array is not all 0 bytes.

if( data.subarray(0x102, 0x10A).some(v => v !== 0) ) {
    return false;
}

@bryc
Copy link
Author

bryc commented Feb 27, 2024

Various one-liners for summing lines of numbers:

sum=(d,x,y=0)=>(d.split('\n').forEach(x=>y+=+x),y)
sum=(d,x,y=0)=>d.split('\n').reduce((a,b)=>a+ +b,0)
sum=(d,x,y=0)=>{for(x of d.split('\n'))y+=+x;return y}

sum(`0.0496962
0.0838341
0.1291209
1.0779618
0.0709846
1.0116026
0.0641498
0.0577544
0.1671571
0.1834607
0.5476034
0.6329983
0.152726`) == 4.2290499

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment