Joshua's Docs - Javascript Snippets

Other Collections of Snippets:

Warning: Most of this page is completely random snippets of JS, either my own, or from a credited source.

Arrays

Shift All Elements of an Array in One Direction

let myArr = ['a', 'b', 'c', 'd'];

/**
 * Shift everything left <--
 * Goal: `["b", "c", "d", "a"]`
 */
myArr.push(myArr.shift());
// Or...
myArr.push(...myArr.splice(0, 1));
// Or...
myArr = [...myArr.slice(1), myArr[0]];

/**
 * Shift everything right -->
 * Goal: `["d", "a", "b", "c"]`
 */
myArr.unshift(myArr.pop());
// Or...
myArr.unshift(...myArr.splice(myArr.length - 1));
// Or...
myArr = [myArr[myArr.length - 1], ...myArr.slice(0, myArr.length - 1)];

Random / Mock / Etc.

Random number in range

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min;
}

// Minified:
function getRandomInt(min,max){
	return f=Math.ceil(min),c=Math.floor(max),Math.floor(Math.random()*(c-f))+f;
}

// If min = 0
function getRandomInt(max) {
	return Math.floor(Math.random() * max);
}

Random Strings / UUIDs / Etc

Random Strings

For a pseudo-random, but simple solution, you can use Math.random() as the source of randomness for a string generator. Example:

function getRandStr(len) {
	return Array(len).fill(0).map(() => Math.random().toString(36).charAt(2)).join('');
}

For true random strings, you need to use something that utilizes a standard crypto library. For example, the native Crypto.getRandomValues() Web API method.

The best solution is often to just use an existing package that can take care of the nuances of creating non-colliding random strings. Something like nanoid is a good pick, with zero-dependencies and an extremely small package size.

UUIDs and GUIDS

See this popular S/O for discussion.

For a package that provides true RFC4122 UUIDs, you might want to check out uuid.

Copy to Clipboard with JavaScript

The recommended way to do this has shifted over time from document.execCommand to the navigator.clipboard API, but browser support is still divided on this. So you might want to have a fallback in place, like in this example:

/**
 * Copy text to clipboard, from textarea
 *  - Note: event param is used as reminder that copy ops require short-lived event triggers
 * @param {HTMLTextAreaElement} textArea
 * @param {MouseEvent | Event} [event]
 * @returns {Promise<boolean>} success
 */
const copyToClipboard = async (textArea, event) => {
	let success = false;
	const text = textArea.value;
	textArea.select();

	if (navigator.clipboard) {
		try {
			await navigator.clipboard.writeText(text);
			success = true;
		} catch (err) {
			console.error('Failed to write to clipboard', err);
		}
	}

	if (!success) {
		success = document.execCommand('copy');
	}

	return success;
};

⚠ - Warning: Clipboard operations (especially those reading from the user's clipboard) typically require either a short-lived event trigger (such as a button click), or an explicit browser permission(s). See the MDN docs for details.

📘 - Good reading on how the clipboard has changed and dealing with new permissions: developers.google.com

Cookie Manipulation

/**
* Cookie Manipulation
* https://plainjs.com/javascript/utilities/set-cookie-get-cookie-and-delete-cookie-5/
*/
function getCookie(name) {
	var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
	return v ? v[2] : null;
}
function setCookie(name, value, days) {
	var d = new Date;
	d.setTime(d.getTime() + 24*60*60*1000*days);
	document.cookie = name + "=" + value + ";path=/;expires=" + d.toGMTString();
}
function deleteCookie(name) { setCookie(name, '', -1); }

General String Methods

Left-Pad

/**
 * Left pad a number or string 
 * @param input {string | number}
 * @param length {number} Length to pad to
 * @param padWith {string} Char to pad with
 */
const leftPad = (input, length, padWith) => {
	let out = input.toString();
	while (out.length < length) {
		out = padWith + out;
	}
	return out;
}

Make proper/title case

Tons of ways to do this, but here is a non-regex solution:

// https://gist.github.com/SonyaMoisset/aa79f51d78b39639430661c03d9b1058#file-title-case-a-sentence-for-loop-wc-js
var toTitleCase = function (str) {
	str = str.toLowerCase().split(' ');
	for (var i = 0; i < str.length; i++) {
		str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);
	}
	return str.join(' ');
};

Or, minified:

function toTitleCase(e){e=e.toLowerCase().split(" ");for(var t=0;t<e.length;t++)e[t]=e[t].charAt(0).toUpperCase()+e[t].slice(1);return e.join(" ")}

Regexp and string methods using regexp

String to Regexp

See if a string "looks" like regex:

/^\/.*\/[igmuy]{0,5}$/.test(myString);

Convert RegExp as string literal, to actual RegExp object

Let's say that I have a Regular Expression, but I have it stored as a string (or maybe I've procedurally generated it as a string from various components). How do I create a true RegExp object (result of new RegExp()) from something like this?:

var strPattern = "/^Hello[\\s-_]World$/i";

Note: \\s is used instead of \s, because you need to double escape (escape regex escape) in JS strings

You cannot use new RegExp(pattern) with something like that; it will fail miserably to produce an actual usable pattern that matches the input.

Custom functions

It is not too difficult to come up with some custom parsers. For example, this function should allow you to pass in string literal reg pattern:

function strToRegExp(strPattern){
	// Test for "/{pattern}/{flags}" input
	const regLikePatt = /^\/(.*)\/([igmuy]{0,5})$/;
	if (regLikePatt.test(strPattern)){
		const pattern = regLikePatt.exec(strPattern)[1];
		const flags = regLikePatt.exec(strPattern)[2];
		return new RegExp(pattern,flags);
	}
	else {
		return new RegExp(strPattern);
	}
}

The only caveat is that you need to double escape your input strings. Example:

strToRegExp("/^Hello[\\s-_]World$/i").test("Hello World");
// > true

DANGEROUS: Eval

You could simply do this:

var regPatt = eval(strPattern);

But it should only be used if you are 100% sure that strPattern does not contain any actual JS. Really not a good reason to use it.

Multiple matches with/without capturing

You can either use the iterative-like property of RegExp.exec():

var regPatt = /f([eo].{1})/g;
var haystack = 'fee foo fee foo fee foo fum';
var execArr;
var matchCount = 0;
while ((execArr = regPatt.exec(haystack)) !== null) {
	console.log('Curr match = ' + execArr[0]);
	console.log('Last two chars = ' + execArr[1]);
	matchCount++;
}
console.log('Total matches = ' + matchCount);

Make sure that A) pattern is declared outside of loop (or else you will end up in infinite loop), and B), using g flag.

Or, much easier (unless you need groups), you can use the String.match() or String.matchAll() methods:

var regPatt = /f[eo].{1}/g;
var haystack = 'fee foo fee foo fee foo fum';
var matchArr = haystack.match(regPatt);

More advanced, but lets us get value from capturing group:

var regPatt = /f([eo].{1})/g;
var haystack = 'fee foo fee foo fee foo fum';
// matchAll actually returns iterator, so use spread to unpack into array
var allMatchArr = [...haystack.matchAll(regPatt)];
for (let x=0; x<allMatchArr.length; x++){
	console.log('Curr match = ' + allMatchArr[x][0]);
	console.log('Last two chars = ' + allMatchArr[x][1]);
}

String Matching - Limitations / gotchas

From MDN:

String.match() won't return groups if the /.../g flag is set. However, you can still use String.matchAll() to get all matches.

String replacement

Using a callback replacer function

You can pass a function as the replacement to String.prototype.replace (MDN). The signature looks like:

/**
 * @param {string} match - The matched *substring*
 * @param {string} p1 - Capture group 1
 * @param {string} p2 - Capture group 2
 * @param {integer} offset - offset (distance) of match from start of *whole* string
 * @param {string} wholeString - The entire string that the replacer is running on (input)
 */
function myReplacer(match, p1, p2, offset, wholeString){
	var finalString = [p1, p2].join(' ');
	console.log(match + ' replaced with '  + finalString);
	return finalString;
}
var newString = 'lorem_ipsum'.replace(/([^_]+)_([^_]+)$/,myReplacer);
// newString will be "lorem ipsum"

... where the number of p# argument is not set and is dependent on the original RegExp.


Tree Object Recurser

interface TreeObj {
	children?: TreeObj[];
}

export function treeObjRecurser(
	input: TreeObj[],
	callback: (currObj: TreeObj, index: number, currTreeObjArr: TreeObj[]) => void | {
		index: number;
		treeObjArr: TreeObj[]
	},
	backwards = false
) {
	const recurse = (treeObjArr: TreeObj[]) => {
		let index = 0;
		const process = () => {
			const treeObj = treeObjArr[index];
			const callbackRes = callback(treeObj, index, treeObjArr);
			if (callbackRes) {
				({treeObjArr, index} = callbackRes);
			}
			if (treeObj.children) {
				recurse(treeObj.children);
			}
		};

		if (backwards) {
			for (index = treeObjArr.length - 1; index >= 0; index--) {
				process();
			}
		} else {
			for (index = 0; index < treeObjArr.length; index++) {
				process();
			}
		}
	};

	recurse(input);
}

Object Merge

Complex Object Merge

/**
 * @author Joshua Tzucker
 */
/**
* @param {object||array} objectA - object, or array of objects to merge together
* @param {object} [objectB] - object to merge into object A
* @param {boolean} [OPT_onlyExisting=false] - Only copy props from B to A, if the prop already exists on A
* @param {function} [OPT_copyFilterFunc] - Optional filter callback that should take the value to be copied over, and return boolean on whether or not it should be
* @param {function} [OPT_copyModFunc] - Optional modification callback that should take the value to be copied over, and modify if it desired before returning
* @returns {object} Merged Object
*/
function objectMerge(objectA, objectB, OPT_onlyExisting, OPT_copyFilterFunc, OPT_copyModFunc){
	let onlyExisting = typeof(OPT_onlyExisting)==='boolean' ? OPT_onlyExisting : false;
	let copyFilter = typeof(OPT_copyFilterFunc)==='function' ? OPT_copyFilterFunc : function(input){
		return true;
	}
	let modFunc = typeof(OPT_copyModFunc)==='function' ? OPT_copyModFunc : function(input){
		return input;
	}
	let mergedObj = {};
	if (Array.isArray(objectA)){
		let objArr = objectA;
		for (var x=0; x<objArr.length; x++){
			mergedObj = objectMerge(mergedObj,objArr[x],onlyExisting,copyFilter,modFunc);
		}
	}
	else {
		for (var x=0; x<Object.keys(objectA).length; x++){
			var attrKey = Object.keys(objectA)[x];
			mergedObj[attrKey] = objectA[attrKey];
		}
		if (typeof(objectB)==='object' && objectB!==null){
			for (var x=0; x<Object.keys(objectB).length; x++){
				var attrKey = Object.keys(objectB)[x];
				if (!onlyExisting || typeof(mergedObj[attrKey])!=='undefined'){
					if (typeof(mergedObj[attrKey])==='object'){
						mergedObj[attrKey] = objectMerge(mergedObj[attrKey],objectB[attrKey],onlyExisting,copyFilter,modFunc);
					}
					else {
						if (copyFilter(objectB[attrKey])){
							mergedObj[attrKey] = modFunc(objectB[attrKey]);
						}
					}
				}
			}
		}
	}
	return mergedObj;
}

// Minified
/**
 * @author Joshua Tzucker
 */
function objectMerge(objectA,objectB,OPT_onlyExisting,OPT_copyFilterFunc,OPT_copyModFunc){let e="boolean"==typeof OPT_onlyExisting&&OPT_onlyExisting,t="function"==typeof OPT_copyFilterFunc?OPT_copyFilterFunc:function(e){return!0},o="function"==typeof OPT_copyModFunc?OPT_copyModFunc:function(e){return e},n={};if(Array.isArray(objectA)){let f=objectA;for(var r=0;r<f.length;r++)n=objectMerge(n,f[r],e,t,o)}else{for(r=0;r<Object.keys(objectA).length;r++){n[f=Object.keys(objectA)[r]]=objectA[f]}if("object"==typeof objectB&&null!==objectB)for(r=0;r<Object.keys(objectB).length;r++){var f=Object.keys(objectB)[r];e&&void 0===n[f]||("object"==typeof n[f]?n[f]=objectMerge(n[f],objectB[f],e,t,o):t(objectB[f])&&(n[f]=o(objectB[f])))}}return n}

Simple Object Merge

/**
 * @param {object||array} objectA - object, or array of objects to merge together
 * @param {object} [objectB] - object to merge into object A
 * @returns {object} Merged Object
 */
function objectMerge(objectA,objectB){
	let mergedObj = {};
	if (Array.isArray(objectA)){
		let objArr = objectA;
		for (var x=0; x<objArr.length; x++){
			mergedObj = objectMerge(mergedObj,objArr[x]);
		}
	}
	else {
		for (let attr in objectA){mergedObj[attr] = objectA[attr]};
		for (let attr in objectB){mergedObj[attr] = objectB[attr]};
	}
	return mergedObj;
}

Object value replacement

/**
 * @author Joshua Tzucker
 */
/**
 * Run a replacer function over an object to modify it
 * Only passes non object values to replacer
 * @param {object} inputObj - the object to replace values in
 * @param {function} replacerFunc - cb func to take value, modify, and return it
 */
function replaceInObj(inputObj, replacerFunc){
	let outputObj = {};
	for (let x=0; x<Object.keys(inputObj).length; x++){
		let key = Object.keys(inputObj)[x];
		let val = inputObj[Object.keys(inputObj)[x]];
		if (Array.isArray(val)){
			for (let y=0; y<val.length; y++){
				val[y] = replacerFunc(val[y]);
			}
		}
		else if (val && typeof(val)==='object'){
			val = replaceInObj(val,replacerFunc);
		}
		else {
			val = replacerFunc(val);
		}
		outputObj[key] = val;
	}
	return outputObj;
}

Debugging events

Monitor all events in Chrome

monitorEvents(document);

Listen for post message

window.addEventListener('message',function(msg){
	console.log(msg);
});

Scraping / Data Prep / Cleanup

var links=[];document.querySelectorAll('a.anchor[name][href]').forEach(function(elem){links.push(/#[^#]*$/.exec(elem.href)[0]);});copy(links.join('\n'));

Amazon Affiliates Payment History Total

var total=0;document.querySelectorAll('.ac-payment-balance-negative').forEach(e=>total+=parseFloat(e.innerText.replace('-$','')));console.log('$'+total.toFixed(2));

Nutrient Guide PDF Cleanup

Convert spaces to tabs (except in name of food). Assumes entries are from PDF that looks like:

item name || calories || fat g || etc...

Code:

var str = `PASTE_ITEMS`;
copy(str.split('\n').map(row => {
	let [dump, name, cals] = /([^\d]+) (.+)/.exec(row);
	name = name.trim();
	cals = cals.trim().replace(/\s/g, '\t');
	return name + '\t' + cals;
}).join('\n'))

Network stuff and File Manipulation

Retrieve Query string Params

/**
 * From: https://davidwalsh.name/query-string-javascript
 * Note that this does *not* return false if param is not found - returns ''
 */
function getUrlParameter(name) {
	name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
	var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
	var results = regex.exec(location.search);
	return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};

Set Query String Params

/**
 * Set a querystring param key/pair value
 * @param {string} url
 * @param {string} paramKey
 * @param {any} paramVal
 * @returns {string} modified URL
 */
const setQueryParam = (url, paramKey, paramVal) => {
	const urlInstance = new URL(url);
	urlInstance.searchParams.set(paramKey, paramVal);
	return url.toString();
}

/**
 * Set multiple query string params by passing an object
 * @param {string} url
 * @param {Record<string, any>} paramPairs
 */
const setQueryParams = (url, paramPairs) => {
	const urlInstance = new URL(url);
	/** @type {Record<string, any>} */
	const existingQueryPairs = {};
	urlInstance.searchParams.forEach((val, key) => {
		existingQueryPairs[key] = val;
	});
	urlInstance.search = new URLSearchParams({
		...existingQueryPairs,
		...paramPairs
	}).toString();
	return urlInstance.toString();
}

Request Body to URL encoded Query String

Old school method:

const objToQueryStr = (obj) => {
	let qs = '';
	for (const prop in obj) {
		const pair = encodeURIComponent(prop) + '=' + encodeURIComponent(obj[prop]);
		qs = qs + (qs.length ? '&' : '') + pair;
	}
	return qs;
}

const objToUrl = (baseUrl, obj) => {
	return baseUrl + (baseUrl.indexOf('?')===-1 ? '?' : '&') + objToQueryStr(obj);
}

Newer, better, more concise, URL() Web API method:

const objToUrl = (baseUrl, obj) => {
	const url = new URL(baseUrl);
	url.search = new URLSearchParams(obj);
	return url.toString();
}

Prompt for File Download - Browser

/**
 * Trigger a file download prompt with given content
 * @see https://davidwalsh.name/javascript-download
 * @param {string} data
 * @param {string} fileName
 * @param {string} [type]
 */
const promptDownload = (data, fileName, type = 'text/plain') => {
	// Create an invisible A element
	const a = document.createElement('a');
	a.style.display = 'none';
	document.body.appendChild(a);

	// Set the HREF to a Blob representation of the data to be downloaded
	a.href = window.URL.createObjectURL(new Blob([data], { type }));

	// Use download attribute to set set desired file name
	a.setAttribute('download', fileName);

	// Trigger download by simulating click
	a.click();

	// Cleanup
	window.URL.revokeObjectURL(a.href);
	document.body.removeChild(a);
};

Generate a CSV (or TVS) Download from a Multi-Dimensional Array

/**
 * Generate a CSV or TSV download from a MD Array
 * @param {Array<Array<any>>} mdArr
 * @param {string} [filename]
 * @param {',' | '\t'} [delimiter]
 */
const downloadCsv = (mdArr, filename, delimiter = ',') => {
	const extension = delimiter === ',' ? 'csv' : 'tsv';
	const mimeString = extension === 'csv' ? 'text/csv' : 'text/tab-separated-values';
	if (!/(\.tsv$|\.csv$)/.test(filename)) {
		filename += `.${extension}`;
	}
	// CSV requires some special escaping
	if (extension === 'csv') {
		mdArr = mdArr.map((arr) => {
			return arr.map((val) => {
				// If it contains a quote, you have to double escape
				val = val.replace(/"/gm, `""`);
				// Wrap entire string (this will also escape commas)
				val = `"${val}"`;
				return val;
			});
		});
	}
	const rawOutput = `data:${mimeString};charset=utf-8,${mdArr.map((r) => r.join(delimiter)).join('\n')}`;
	const link = document.createElement('a');
	link.setAttribute('href', encodeURI(rawOutput));
	link.setAttribute('download', filename);
	link.style.display = 'none';
	document.body.appendChild(link);
	link.click(); // Prompt download
	link.parentNode.removeChild(link); // Cleanup
};

Convert File / Image to Base64

/**
 * Get URL response as base64
 * @param {string} url - URL to convert
 * @param {boolean} [omitDeclaration] - remove the `data:...` declaration prefix
 * @returns {Promise<{dataStr: string, mimeStr: string}>} base64 results
 */
async function urlToBase64(url, omitDeclaration = false) {
	const res = await fetch(url);
	const blob = await res.blob();
	return new Promise((resolve, reject) => {
		const reader = new FileReader();
		reader.onloadend = () => {
			const declarationPatt = /^data:([^;]+)[^,]+base64,/i;
			let dataStr = /** @type {string} */ (reader.result);
			const mimeStr = dataStr.match(declarationPatt)[1];
			if (omitDeclaration) {
				dataStr = dataStr.replace(declarationPatt, '');
			}

			resolve({
				dataStr,
				mimeStr
			});
		};
		reader.onerror = reject;
		reader.readAsDataURL(blob);
	});
}

@See: Helpful S/O Answer for this, and other approaches.

Time conversion / Date and Time

MS to parts

/**
 * Convert Ms to parts
 * @param ms {number} - Milliseconds to convert
 */
function msToParts(ms) {
	const days = Math.floor(ms / 86400000);
	const hrs = Math.floor(ms % 86400000 / 3600000);
	const mins = Math.floor(ms % 86400000 % 3600000 / 60000);
	const secs = Math.floor(ms % 86400000 % 3600000 % 60000 / 1000);
	const remainMs = Math.floor(ms % 86400000 % 3600000 % 60000 % 1000);
	return {
		days,
		hrs,
		mins,
		secs,
		ms: remainMs
	};
}

JavaScript Date To ISO-8601

If you are just looking for a flavor of ISO-8601, technically .toISOString() adheres to this requirement. E.g:

(new Date()).toISOString();
// "2020-02-04T11:32:59.621Z"

However, there are three big issues with this:

  • It assumes UTC, not your local timezone
  • Because of the above, it also does not end with offset from UTC
  • It displays a fractional second

This is in stark contrast to how a lot of other systems use ISO-8601. For example, it is common to see the same date as above, represented by 2020-02-04T03:32:59-0800. That is the same date and time, but with a local timezone (PST), no fractional seconds, and ending with offset from UTC per the local timezone (PST is -8, except during daylight savings).

Getting ISO-8601 with offset in JavaScript

I'm not just going to copy and paste someone else's code here, but I have two recommendations:

  • Either:
    • Highly recommended: Use a full-fledged library, like moment.js to handle formatting
      • In fact, the default moment().format() produces a nice ISO-8601 with offset
    • Or, use verified code, like this S/O answer.

Hashing

Java Hash Code

Fast, but very insecure. Should only be used non-security related stuff.

/**
 * Creates a quick hash of a string, based on Java's `.hashCode()`
 * Credit: https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
 * @param str {string} - String to create hash from
 */
function hashCode(str) {
	let hash = 0;
	if (str.length == 0) return hash;
	for (let i = 0; i < str.length; i++) {
		const char = str.charCodeAt(i);
		hash = ((hash << 5) - hash) + char;
		hash = hash & hash; // Convert to 32bit integer
	}
	return hash;
}

https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/

Regular hash digest

Whipped this up by combining a few approaches:

/**
 * Computes a hash digest (represented as string) from input string
 * Based on https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
 * And - https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
 * @param str {string} - Input string to hash
 * @param hashAlg {'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'} - Crypto alg hash function to use
 * @param [txtEncoding] {'utf8' | 'utf16'} - Optional: Set the encoding of the text for the buffer used to compute the digest. Defaults to UTF-8.
 * @example getHashDigest('Hello World','SHA-256');
 */
async function getHashDigest(str, hashAlg, txtEncoding) {
	let buff;
	// Fallback to old method if newer TextEncoder is not available, or UTF-16 (TextEncoder only does UTF8)
	if (typeof (window.TextEncoder) !== 'function' || txtEncoding === 'utf16') {
		if (txtEncoding === 'utf16') {
			buff = new ArrayBuffer(str.length * 2);
			const buffView = new Uint16Array(buff);
			for (var i = 0, strLen = str.length; i < strLen; i++) {
				buffView[i] = str.charCodeAt(i);
			}
		} else {
			buff = new ArrayBuffer(str.length);
			const buffView = new Uint8Array(buff);
			for (var i = 0, strLen = str.length; i < strLen; i++) {
				buffView[i] = str.charCodeAt(i);
			}
		}
	} else {
		const encoder = new TextEncoder();
		buff = encoder.encode(str);
	}
	const hashArr = Array.from(new Uint8Array(await crypto.subtle.digest(hashAlg, buff)));
	return hashArr.map(b => b.toString(16).padStart(2, '0')).join('');
}

// Minified:
async function getHashDigest(e, t, n) { let r; if ("function" != typeof window.TextEncoder || "utf16" === n) if ("utf16" === n) { r = new ArrayBuffer(2 * e.length); const t = new Uint16Array(r); for (var o = 0, a = e.length; o < a; o++)t[o] = e.charCodeAt(o) } else { r = new ArrayBuffer(e.length); const t = new Uint8Array(r); for (o = 0, a = e.length; o < a; o++)t[o] = e.charCodeAt(o) } else { r = (new TextEncoder).encode(e) } return Array.from(new Uint8Array(await crypto.subtle.digest(t, r))).map(e => e.toString(16).padStart(2, "0")).join("") }

NodeJS

Write to a debug file

One-liner (with dynamic import):

// Await
(await import('fs')).writeFileSync(`${__dirname}/.debug-log`, `${JSON.stringify(myThing)}\n`, {flag: 'a'});

// Require
require('fs').writeFileSync(`${__dirname}/.debug-log`, `${JSON.stringify(myThing)}\n`, {flag: 'a'});

Filepaths - Convert to Posix Slash Style

const path = require('path');

const forcedPosix = myPathStr.split(path.sep).join(path.posix.sep);

Simple file looping with read/write

If you want to iterate over the files in a directory and do something with the contents of each, an easy built-in with NodeJS is the methods provided by fs(FileSystem). For example:

const fs = require('fs');
const path = require('path');
const dir = '.';
fs.readdir(dir, (err, fileList) => {
	fileList.forEach(fileName => {
		const filePath = path.normalize(`${dir}/${fileName}`);
		let content = fs.readFileSync(filePath, 'utf-8');
		// Do something
		// e.g.: `content = content.toUpperCase();`
		fs.writeFileSync(filePath, content);
	});
})

Hacker Rank

Simple script to redirect output to local file, and mirror the output to the console as it runs

const fs = require('fs');

// Add OUTPUT_PATH var
var outFilename = './output.txt';
process.env.OUTPUT_PATH = outFilename;
// Make sure file exists and empty it out first
fs.writeFileSync(outFilename,'',{
	encoding: 'utf-8'
});
// Get initial file stats
var outFileStats = fs.statSync(outFilename);
// Cache changes so we can filter out file "touch" events vs actual content changes
var fileChangeCache = {fileSize:0,diffSize:0,str:''};
// Watch file
// Largely inspired by https://coderwall.com/p/2pvepq/tail-f-in-node-js
fs.watch(outFilename, function(evt, filename) {
	// Warning: 'change' event double fires, once for write, then again for closing out
	// Delay is necessary due to timing of change event
	setTimeout(function(){
		var updatedFileStats = fs.statSync(outFilename);
		fs.open(outFilename, 'r', function(err, fileDescriptor) {
			var newDataLength = updatedFileStats.size - outFileStats.size;
			var buff = Buffer.alloc(newDataLength,null,'utf-8');
			fs.read(fileDescriptor, buff, 0, newDataLength, outFileStats.size, function (err, bytesRead, newDataBuff) {
				if (err) {
					console.warn(err);
				};
				var newDataString = newDataBuff.toString();
				if (!(newDataString === fileChangeCache.str && fileChangeCache.diffSize === newDataLength && fileChangeCache.fileSize === updatedFileStats.size)){
					console.log(newDataString);
					fileChangeCache = {
						str: newDataString,
						fileSize: updatedFileStats.size,
						diffSize: newDataLength
					}
				}
			});
		});
	},100);
});
// Redirect windows CTRL+C to stdin-end
process.on('SIGINT',function(){
	// Emit EOF / end event - https://nodejs.org/api/stream.html#stream_event_end
	process.stdin.emit('end');
});

Preserve Bookmarklet comments while transpiling

const fs = require('fs');

const originalFileContents = fs.readFileSync(`${__dirname}/index.js`).toString();
const bookmarkletCommentBlock = originalFileContents.match(/\/\/ ==Bookmarklet(?:.|[\r\n])+\/Bookmarklet==/gim)[0];
fs.writeFileSync(`${__dirname}/index.build.js`, '\n\n' + bookmarkletCommentBlock, {
	flag: 'a'
});

Algorithms

Generate k-sized subset combinations out of a larger set

// @ts-check
/**
 * Adapted from https://gist.github.com/axelpale/3118596
 * @template T
 * @param {Array<T>} set The input array to generate k-sized permutations from
 * @param {number} kSize The size of each subset permutation
 * @returns {T[][]}
 */
function kSubsetCombinations(set, kSize) {
	/** @type {T[][]} */
	let combs = [];
	/** @type {T[][]} */
	let tailCombs = [];
	/** @type {T[]} */
	let head;

	// There is no way to take e.g. sets of 5 elements from
	// a set of 4.
	if (kSize > set.length || kSize <= 0) {
		return [];
	}

	// K-sized set has only one K-sized subset.
	if (kSize == set.length) {
		return [set];
	}

	// There is N 1-sized subsets in a N-sized set.
	if (kSize == 1) {
		combs = [];
		for (let i = 0; i < set.length; i++) {
			combs.push([set[i]]);
		}
		return combs;
	}

	combs = [];
	for (let i = 0; i < set.length - kSize + 1; i++) {
		// head is a list that includes only our current element.
		head = set.slice(i, i + 1);
		// We take smaller combinations from the subsequent elements
		tailCombs = kSubsetCombinations(set.slice(i + 1), kSize - 1);
		// For each (k-1)-combination we join it with the current
		// and store it to the set of k-combinations.
		for (let j = 0; j < tailCombs.length; j++) {
			combs.push(head.concat(tailCombs[j]));
		}
	}
	return combs;
}
Markdown Source Last Updated:
Sun Oct 20 2024 10:22:22 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Mon Aug 19 2019 17:06:24 GMT+0000 (Coordinated Universal Time)
© 2024 Joshua Tzucker, Built with Gatsby
Feedback