Joshua's Cheatsheets-Javascript Snippets
Light
help

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);
}

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); }

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, ' '));
};

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
	var regLikePatt = /^\/(.*)\/([igmuy]{0,5})$/;
	if (regLikePatt.test(strPattern)){
		var pattern = regLikePatt.exec(strPattern)[1];
		var 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);

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]);
}

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.


Object Merge

Complex

/**
 * @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

/**
 * @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
 * @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

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));

Network stuff

Request Body to URL encoded Query String

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);
}

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

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');
});
Markdown Source Last Updated:
Sun Mar 08 2020 14:17:46 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Mon Aug 19 2019 17:06:24 GMT+0000 (Coordinated Universal Time)
© 2020 Joshua Tzucker, Built with Gatsby