diff --git a/snippets/createDirIfNotExists.md b/snippets/createDirIfNotExists.md new file mode 100644 index 000000000..d31844ea5 --- /dev/null +++ b/snippets/createDirIfNotExists.md @@ -0,0 +1,14 @@ +### createDirIfNotExists + +Creates a directory, if it does not exist. + +Use `fs.existsSync()` to check if the directory exists, `fs.mkdirSync()` to create it. + +```js +const fs = require('fs'); +const createDirIfNotExists = dir => !fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined; +``` + +```js +createDirIfNotExists('test'); // creates the directory 'test', if it doesn't exist +``` diff --git a/tag_database b/tag_database index 1f04eb71a..0636ee018 100644 --- a/tag_database +++ b/tag_database @@ -39,6 +39,7 @@ copyToClipboard:browser,string,advanced countBy:array,object,intermediate counter:browser,advanced countOccurrences:array,intermediate +createDirIfNotExists:node,beginner createElement:browser,utility,beginner createEventHub:browser,event,advanced CSVToArray:string,array,utility,intermediate diff --git a/test/_30s.js b/test/_30s.js index 85bc2d834..fd1b1fee9 100644 --- a/test/_30s.js +++ b/test/_30s.js @@ -1,66 +1,28 @@ const fs = typeof require !== "undefined" && require('fs'); const crypto = typeof require !== "undefined" && require('crypto'); -const CSVToArray = (data, delimiter = ',', omitFirstRow = false) => - data - .slice(omitFirstRow ? data.indexOf('\n') + 1 : 0) - .split('\n') - .map(v => v.split(delimiter)); -const CSVToJSON = (data, delimiter = ',') => { - const titles = data.slice(0, data.indexOf('\n')).split(delimiter); - return data - .slice(data.indexOf('\n') + 1) - .split('\n') - .map(v => { - const values = v.split(delimiter); - return titles.reduce((obj, title, index) => ((obj[title] = values[index]), obj), {}); - }); -}; -const JSONToFile = (obj, filename) => - fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2)); -const JSONtoCSV = (arr, columns, delimiter = ',') => - [ - columns.join(delimiter), - ...arr.map(obj => - columns.reduce( - (acc, key) => `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`, - '' - ) - ) - ].join('\n'); -const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); -const URLJoin = (...args) => - args - .join('/') - .replace(/[\/]+/g, '/') - .replace(/^(.+):\//, '$1://') - .replace(/^file:/, 'file:/') - .replace(/\/(\?|&|#[^!])/g, '$1') - .replace(/\?/g, '&') - .replace('&', '?'); -const UUIDGeneratorBrowser = () => - ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => - (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16) - ); - -const UUIDGeneratorNode = () => - ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => - (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16) - ); const all = (arr, fn = Boolean) => arr.every(fn); + const allEqual = arr => arr.every(val => val === arr[0]); + const any = (arr, fn = Boolean) => arr.some(fn); + const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon; + const arrayToCSV = (arr, delimiter = ',') => arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n'); + const arrayToHtmlList = (arr, listID) => (el => ( (el = document.querySelector('#' + listID)), (el.innerHTML += arr.map(item => `
  • ${item}
  • `).join('')) ))(); + const ary = (fn, n) => (...args) => fn(...args.slice(0, n)); + const atob = str => Buffer.from(str, 'base64').toString('binary'); + const attempt = (fn, ...args) => { try { return fn(...args); @@ -68,15 +30,21 @@ const attempt = (fn, ...args) => { return e instanceof Error ? e : new Error(e); } }; + const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length; + const averageBy = (arr, fn) => arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) / arr.length; + const bifurcate = (arr, filter) => arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]); + const bifurcateBy = (arr, fn) => arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [[], []]); + const bind = (fn, context, ...boundArgs) => (...args) => fn.apply(context, [...boundArgs, ...args]); + const bindAll = (obj, ...fns) => fns.forEach( fn => ( @@ -86,8 +54,10 @@ const bindAll = (obj, ...fns) => }) ) ); + const bindKey = (context, fn, ...boundArgs) => (...args) => context[fn].apply(context, [...boundArgs, ...args]); + const binomialCoefficient = (n, k) => { if (Number.isNaN(n) || Number.isNaN(k)) return NaN; if (k < 0 || k > n) return 0; @@ -98,16 +68,23 @@ const binomialCoefficient = (n, k) => { for (let j = 2; j <= k; j++) res *= (n - j + 1) / j; return Math.round(res); }; + const bottomVisible = () => document.documentElement.clientHeight + window.scrollY >= (document.documentElement.scrollHeight || document.documentElement.clientHeight); + const btoa = str => Buffer.from(str, 'binary').toString('base64'); + const byteSize = str => new Blob([str]).size; const call = (key, ...args) => context => context[key](...args); + const capitalize = ([first, ...rest], lowerRest = false) => first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join('')); + const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase()); + const castArray = val => (Array.isArray(val) ? val : [val]); + const chainAsync = fns => { let curr = 0; const last = fns[fns.length - 1]; @@ -117,15 +94,21 @@ const chainAsync = fns => { }; next(); }; + const chunk = (arr, size) => Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => arr.slice(i * size, i * size + size) ); + const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)); + const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags); + const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_)); + const coalesceFactory = valid => (...args) => args.find(valid); const collectInto = fn => (...args) => fn(args); + const colorize = (...args) => ({ black: `\x1b[30m${args.join(' ')}`, red: `\x1b[31m${args.join(' ')}`, @@ -144,11 +127,15 @@ const colorize = (...args) => ({ bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`, bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m` }); + const compact = arr => arr.filter(Boolean); const compactWhitespace = str => str.replace(/\s{2,}/g, ' '); const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args))); + const composeRight = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args))); + const converge = (converger, fns) => (...args) => converger(...fns.map(fn => fn.apply(null, args))); + const copyToClipboard = str => { const el = document.createElement('textarea'); el.value = str; @@ -166,12 +153,13 @@ const copyToClipboard = str => { document.getSelection().addRange(selected); } }; + const countBy = (arr, fn) => arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => { acc[val] = (acc[val] || 0) + 1; return acc; }, {}); -const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0); + const counter = (selector, start, end, step = 1, duration = 2000) => { let current = start, _step = (end - start) * step < 0 ? -step : step, @@ -183,11 +171,18 @@ const counter = (selector, start, end, step = 1, duration = 2000) => { }, Math.abs(Math.floor(duration / (end - start)))); return timer; }; + +const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0); + + +const createDirIfNotExists = dir => !fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined; + const createElement = str => { const el = document.createElement('div'); el.innerHTML = str; return el.firstElementChild; }; + const createEventHub = () => ({ hub: Object.create(null), emit(event, data) { @@ -202,11 +197,32 @@ const createEventHub = () => ({ if (i > -1) this.hub[event].splice(i, 1); } }); + +const CSVToArray = (data, delimiter = ',', omitFirstRow = false) => + data + .slice(omitFirstRow ? data.indexOf('\n') + 1 : 0) + .split('\n') + .map(v => v.split(delimiter)); + +const CSVToJSON = (data, delimiter = ',') => { + const titles = data.slice(0, data.indexOf('\n')).split(delimiter); + return data + .slice(data.indexOf('\n') + 1) + .split('\n') + .map(v => { + const values = v.split(delimiter); + return titles.reduce((obj, title, index) => ((obj[title] = values[index]), obj), {}); + }); +}; + const currentURL = () => window.location.href; + const curry = (fn, arity = fn.length, ...args) => arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args); + const dayOfYear = date => Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24); + const debounce = (fn, ms = 0) => { let timeoutId; return function(...args) { @@ -214,8 +230,10 @@ const debounce = (fn, ms = 0) => { timeoutId = setTimeout(() => fn.apply(this, args), ms); }; }; + const decapitalize = ([first, ...rest], upperRest = false) => first.toLowerCase() + (upperRest ? rest.join('').toUpperCase() : rest.join('')); + const deepClone = obj => { let clone = Object.assign({}, obj); Object.keys(clone).forEach( @@ -223,12 +241,16 @@ const deepClone = obj => { ); return Array.isArray(obj) ? (clone.length = obj.length) && Array.from(clone) : clone; }; + const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v))); + const deepFreeze = obj => Object.keys(obj).forEach( prop => !(obj[prop] instanceof Object) || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop]) ) || Object.freeze(obj); + + const deepMapKeys = (obj, f) => Array.isArray(obj) ? obj.map(val => deepMapKeys(val, f)) @@ -240,23 +262,33 @@ const deepMapKeys = (obj, f) => return acc; }, {}) : obj; + const defaults = (obj, ...defs) => Object.assign({}, obj, ...defs.reverse(), obj); + const defer = (fn, ...args) => setTimeout(fn, 1, ...args); + const degreesToRads = deg => (deg * Math.PI) / 180.0; + const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args); + const detectDeviceType = () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ? 'Mobile' : 'Desktop'; + const difference = (a, b) => { const s = new Set(b); return a.filter(x => !s.has(x)); }; + const differenceBy = (a, b, fn) => { const s = new Set(b.map(fn)); return a.filter(x => !s.has(fn(x))); }; + const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1); + + const dig = (obj, target) => target in obj ? obj[target] @@ -264,19 +296,27 @@ const dig = (obj, target) => if (acc !== undefined) return acc; if (typeof val === 'object') return dig(val, target); }, undefined); + const digitize = n => [...`${n}`].map(i => parseInt(i)); + const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0); + const drop = (arr, n = 1) => arr.slice(n); + const dropRight = (arr, n = 1) => arr.slice(0, -n); + const dropRightWhile = (arr, func) => { while (arr.length > 0 && !func(arr[arr.length - 1])) arr = arr.slice(0, -1); return arr; }; + const dropWhile = (arr, func) => { while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1); return arr; }; + const elementContains = (parent, child) => parent !== child && parent.contains(child); + const elementIsVisibleInViewport = (el, partiallyVisible = false) => { const { top, left, bottom, right } = el.getBoundingClientRect(); const { innerHeight, innerWidth } = window; @@ -285,6 +325,7 @@ const elementIsVisibleInViewport = (el, partiallyVisible = false) => { ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth)) : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth; }; + const elo = ([...ratings], kFactor = 32, selfRating) => { const [a, b] = ratings; const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400)); @@ -301,6 +342,7 @@ const elo = ([...ratings], kFactor = 32, selfRating) => { } return ratings; }; + const equals = (a, b) => { if (a === b) return true; if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime(); @@ -311,6 +353,7 @@ const equals = (a, b) => { if (keys.length !== Object.keys(b).length) return false; return keys.every(k => equals(a[k], b[k])); }; + const escapeHTML = str => str.replace( /[&<>'"]/g, @@ -323,8 +366,11 @@ const escapeHTML = str => '"': '"' }[tag] || tag) ); + const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1); + const extendHex = shortHex => '#' + shortHex @@ -332,6 +378,8 @@ const extendHex = shortHex => .split('') .map(x => x + x) .join(''); + + const factorial = n => n < 0 ? (() => { @@ -340,28 +388,38 @@ const factorial = n => : n <= 1 ? 1 : n * factorial(n - 1); + const fibonacci = n => Array.from({ length: n }).reduce( (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), [] ); + const filterFalsy = arr => arr.filter(Boolean); + const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i)); + const filterNonUniqueBy = (arr, fn) => arr.filter((v, i) => arr.every((x, j) => (i === j) === fn(v, x, i, j))); + const findKey = (obj, fn) => Object.keys(obj).find(key => fn(obj[key], key, obj)); + const findLast = (arr, fn) => arr.filter(fn).pop(); + const findLastIndex = (arr, fn) => arr .map((val, i) => [i, val]) .filter(([i, val]) => fn(val, i, arr)) .pop()[0]; + const findLastKey = (obj, fn) => Object.keys(obj) .reverse() .find(key => fn(obj[key], key, obj)); + const flatten = (arr, depth = 1) => arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []); + const flattenObject = (obj, prefix = '') => Object.keys(obj).reduce((acc, k) => { const pre = prefix.length ? prefix + '.' : ''; @@ -370,16 +428,13 @@ const flattenObject = (obj, prefix = '') => return acc; }, {}); const flip = fn => (first, ...rest) => fn(...rest, first); + const forEachRight = (arr, callback) => arr .slice(0) .reverse() .forEach(callback); -const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj)); -const forOwnRight = (obj, fn) => - Object.keys(obj) - .reverse() - .forEach(key => fn(obj[key], key, obj)); + const formatDuration = ms => { if (ms < 0) ms = -ms; const time = { @@ -394,25 +449,38 @@ const formatDuration = ms => { .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`) .join(', '); }; + +const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj)); + +const forOwnRight = (obj, fn) => + Object.keys(obj) + .reverse() + .forEach(key => fn(obj[key], key, obj)); + const fromCamelCase = (str, separator = '_') => str .replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2') .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2') .toLowerCase(); + const functionName = fn => (console.debug(fn.name), fn); + const functions = (obj, inherited = false) => (inherited ? [...Object.keys(obj), ...Object.keys(Object.getPrototypeOf(obj))] : Object.keys(obj) ).filter(key => typeof obj[key] === 'function'); + const gcd = (...arr) => { const _gcd = (x, y) => (!y ? x : gcd(y, x % y)); return [...arr].reduce((a, b) => _gcd(a, b)); }; + const geometricProgression = (end, start = 1, step = 2) => Array.from({ length: Math.floor(Math.log(end / start) / Math.log(step)) + 1 }).map( (v, i) => start * step ** i ); + const get = (from, ...selectors) => [...selectors].map(s => s @@ -421,13 +489,17 @@ const get = (from, ...selectors) => .filter(t => t !== '') .reduce((prev, cur) => prev && prev[cur], from) ); + const getColonTimeFromDate = date => date.toTimeString().slice(0, 8); + const getDaysDiffBetweenDates = (dateInitial, dateFinal) => (dateFinal - dateInitial) / (1000 * 3600 * 24); + const getImages = (el, includeDuplicates = false) => { const images = [...el.getElementsByTagName('img')].map(img => img.getAttribute('src')); return includeDuplicates ? images : [...new Set(images)]; }; + const getMeridiemSuffixOfInteger = num => num === 0 || num === 24 ? 12 + 'am' @@ -436,27 +508,36 @@ const getMeridiemSuffixOfInteger = num => : num < 12 ? (num % 12) + 'am' : (num % 12) + 'pm'; + const getScrollPosition = (el = window) => ({ x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft, y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop }); + const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName]; + const getType = v => v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase(); + const getURLParameters = url => (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce( (a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), {} ); + const groupBy = (arr, fn) => arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => { acc[val] = (acc[val] || []).concat(arr[i]); return acc; }, {}); + const hammingDistance = (num1, num2) => ((num1 ^ num2).toString(2).match(/1/g) || '').length; + const hasClass = (el, className) => el.classList.contains(className); + const hasFlags = (...flags) => flags.every(flag => process.argv.includes(/^-{1,2}/.test(flag) ? flag : '--' + flag)); + const hashBrowser = val => crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => { let hexes = [], @@ -466,6 +547,7 @@ const hashBrowser = val => return hexes.join(''); }); + const hashNode = val => new Promise(resolve => setTimeout( @@ -479,7 +561,9 @@ const hashNode = val => 0 ) ); + const head = arr => arr[0]; + const hexToRGB = hex => { let alpha = false, h = hex.slice(hex.startsWith('#') ? 1 : 0); @@ -499,7 +583,9 @@ const hexToRGB = hex => { ')' ); }; + const hide = (...el) => [...el].forEach(e => (e.style.display = 'none')); + const httpGet = (url, callback, err = console.error) => { const request = new XMLHttpRequest(); request.open('GET', url, true); @@ -507,6 +593,7 @@ const httpGet = (url, callback, err = console.error) => { request.onerror = () => err(request); request.send(); }; + const httpPost = (url, data, callback, err = console.error) => { const request = new XMLHttpRequest(); request.open('POST', url, true); @@ -515,45 +602,62 @@ const httpPost = (url, data, callback, err = console.error) => { request.onerror = () => err(request); request.send(data); }; + const httpsRedirect = () => { if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]); }; + const hz = (fn, iterations = 100) => { const before = performance.now(); for (let i = 0; i < iterations; i++) fn(); return (1000 * iterations) / (performance.now() - before); }; -const inRange = (n, start, end = null) => { - if (end && start > end) [end, start] = [start, end]; - return end == null ? n >= 0 && n < start : n >= start && n < end; -}; + const indentString = (str, count, indent = ' ') => str.replace(/^/gm, indent.repeat(count)); + const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []); + const initial = arr => arr.slice(0, -1); + const initialize2DArray = (w, h, val = null) => Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val)); + const initializeArrayWithRange = (end, start = 0, step = 1) => Array.from({ length: Math.ceil((end - start + 1) / step) }, (v, i) => i * step + start); + const initializeArrayWithRangeRight = (end, start = 0, step = 1) => Array.from({ length: Math.ceil((end + 1 - start) / step) }).map( (v, i, arr) => (arr.length - i - 1) * step + start ); + const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val); + const initializeNDArray = (val, ...args) => args.length === 0 ? val : Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1))); + +const inRange = (n, start, end = null) => { + if (end && start > end) [end, start] = [start, end]; + return end == null ? n >= 0 && n < start : n >= start && n < end; +}; + const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString); + const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString); + const intersection = (a, b) => { const s = new Set(b); return a.filter(x => s.has(x)); }; + const intersectionBy = (a, b, fn) => { const s = new Set(b.map(fn)); return a.filter(x => s.has(fn(x))); }; + const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1); + const invertKeyValues = (obj, fn) => Object.keys(obj).reduce((acc, key) => { const val = fn ? fn(obj[key]) : obj[key]; @@ -561,9 +665,13 @@ const invertKeyValues = (obj, fn) => acc[val].push(key); return acc; }, {}); + const is = (type, val) => ![, null].includes(val) && val.constructor === type; + const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str); + const isAfterDate = (dateA, dateB) => dateA > dateB; + const isAnagram = (str1, str2) => { const normalize = str => str @@ -574,12 +682,19 @@ const isAnagram = (str1, str2) => { .join(''); return normalize(str1) === normalize(str2); }; + const isArrayLike = obj => obj != null && typeof obj[Symbol.iterator] === 'function'; + const isBeforeDate = (dateA, dateB) => dateA < dateB; + const isBoolean = val => typeof val === 'boolean'; + const isBrowser = () => ![typeof window, typeof document].includes('undefined'); + const isBrowserTabFocused = () => !document.hidden; + const isDivisible = (dividend, divisor) => dividend % divisor === 0; + const isDuplexStream = val => val !== null && typeof val === 'object' && @@ -588,34 +703,51 @@ const isDuplexStream = val => typeof val._readableState === 'object' && typeof val._write === 'function' && typeof val._writableState === 'object'; + const isEmpty = val => val == null || !(Object.keys(val) || val).length; + const isEven = num => num % 2 === 0; + const isFunction = val => typeof val === 'function'; + const isLowerCase = str => str === str.toLowerCase(); + const isNegativeZero = val => val === 0 && 1 / val === -Infinity; + const isNil = val => val === undefined || val === null; + const isNull = val => val === null; + const isNumber = val => typeof val === 'number'; + const isObject = obj => obj === Object(obj); + const isObjectLike = val => val !== null && typeof val === 'object'; + const isPlainObject = val => !!val && typeof val === 'object' && val.constructor === Object; + const isPrime = num => { const boundary = Math.floor(Math.sqrt(num)); for (var i = 2; i <= boundary; i++) if (num % i === 0) return false; return num >= 2; }; + const isPrimitive = val => Object(val) !== val; + const isPromiseLike = obj => obj !== null && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; + const isReadableStream = val => val !== null && typeof val === 'object' && typeof val.pipe === 'function' && typeof val._read === 'function' && typeof val._readableState === 'object'; + const isSameDate = (dateA, dateB) => dateA.toISOString() === dateB.toISOString(); + const isSorted = arr => { let direction = -(arr[0] - arr[1]); for (let [i, val] of arr.entries()) { @@ -624,12 +756,19 @@ const isSorted = arr => { else if ((val - arr[i + 1]) * direction > 0) return 0; } }; + const isStream = val => val !== null && typeof val === 'object' && typeof val.pipe === 'function'; + const isString = val => typeof val === 'string'; + const isSymbol = val => typeof val === 'symbol'; + const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env; + const isUndefined = val => val === undefined; + const isUpperCase = str => str === str.toUpperCase(); + const isValidJSON = str => { try { JSON.parse(str); @@ -638,12 +777,14 @@ const isValidJSON = str => { return false; } }; + const isWritableStream = val => val !== null && typeof val === 'object' && typeof val.pipe === 'function' && typeof val._write === 'function' && typeof val._writableState === 'object'; + const join = (arr, separator = ',', end = separator) => arr.reduce( (acc, val, i) => @@ -654,18 +795,38 @@ const join = (arr, separator = ',', end = separator) => : acc + val + separator, '' ); + +const JSONtoCSV = (arr, columns, delimiter = ',') => + [ + columns.join(delimiter), + ...arr.map(obj => + columns.reduce( + (acc, key) => `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`, + '' + ) + ) + ].join('\n'); + + +const JSONToFile = (obj, filename) => + fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2)); + const last = arr => arr[arr.length - 1]; + const lcm = (...arr) => { const gcd = (x, y) => (!y ? x : gcd(y, x % y)); const _lcm = (x, y) => (x * y) / gcd(x, y); return [...arr].reduce((a, b) => _lcm(a, b)); }; + const longestItem = (...vals) => vals.reduce((a, x) => (x.length > a.length ? x : a)); + const lowercaseKeys = obj => Object.keys(obj).reduce((acc, key) => { acc[key.toLowerCase()] = obj[key]; return acc; }, {}); + const luhnCheck = num => { let arr = (num + '') .split('') @@ -676,28 +837,35 @@ const luhnCheck = num => { sum += lastDigit; return sum % 10 === 0; }; + const mapKeys = (obj, fn) => Object.keys(obj).reduce((acc, k) => { acc[fn(obj[k], k, obj)] = obj[k]; return acc; }, {}); + const mapObject = (arr, fn) => (a => ( (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {}) ))(); + const mapString = (str, fn) => str .split('') .map((c, i) => fn(c, i, str)) .join(''); + const mapValues = (obj, fn) => Object.keys(obj).reduce((acc, k) => { acc[k] = fn(obj[k], k, obj); return acc; }, {}); + const mask = (cc, num = 4, mask = '*') => `${cc}`.slice(-num).padStart(`${cc}`.length, mask); + const matches = (obj, source) => Object.keys(source).every(key => obj.hasOwnProperty(key) && obj[key] === source[key]); + const matchesWith = (obj, source, fn) => Object.keys(source).every( key => @@ -705,14 +873,19 @@ const matchesWith = (obj, source, fn) => ? fn(obj[key], source[key], key, obj, source) : obj[key] == source[key] ); + const maxBy = (arr, fn) => Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn])); + const maxDate = (...dates) => new Date(Math.max.apply(null, ...dates)); + const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n); + const median = arr => { const mid = Math.floor(arr.length / 2), nums = [...arr].sort((a, b) => a - b); return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; }; + const memoize = fn => { const cache = new Map(); const cached = function(val) { @@ -721,6 +894,7 @@ const memoize = fn => { cached.cache = cache; return cached; }; + const merge = (...objs) => [...objs].reduce( (acc, obj) => @@ -730,10 +904,15 @@ const merge = (...objs) => }, {}), {} ); + const midpoint = ([x1, y1], [x2, y2]) => [(x1 + x2) / 2, (y1 + y2) / 2]; + const minBy = (arr, fn) => Math.min(...arr.map(typeof fn === 'function' ? fn : val => val[fn])); + const minDate = (...dates) => new Date(Math.min.apply(null, ...dates)); + const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n); + const mostPerformant = (fns, iterations = 10000) => { const times = fns.map(fn => { const before = performance.now(); @@ -742,17 +921,26 @@ const mostPerformant = (fns, iterations = 10000) => { }); return times.indexOf(Math.min(...times)); }; + const negate = func => (...args) => !func(...args); + const nest = (items, id = null, link = 'parent_id') => items .filter(item => item[link] === id) .map(item => ({ ...item, children: nest(items, item.id) })); + const nodeListToArray = nodeList => [...nodeList]; + const none = (arr, fn = Boolean) => !arr.some(fn); + const nthArg = n => (...args) => args.slice(n)[0]; + const nthElement = (arr, n = 0) => (n === -1 ? arr.slice(n) : arr.slice(n, n + 1))[0]; + const objectFromPairs = arr => arr.reduce((a, [key, val]) => ((a[key] = val), a), {}); + const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]); + const observeMutations = (element, callback, options) => { const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m))); observer.observe( @@ -771,21 +959,36 @@ const observeMutations = (element, callback, options) => { ); return observer; }; + const off = (el, evt, fn, opts = false) => el.removeEventListener(evt, fn, opts); + const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)]; + const omit = (obj, arr) => Object.keys(obj) .filter(k => !arr.includes(k)) .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); + const omitBy = (obj, fn) => Object.keys(obj) .filter(k => !fn(obj[k], k)) .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); + const on = (el, evt, fn, opts = {}) => { const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e); el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false); if (opts.target) return delegatorFn; }; + +const once = fn => { + let called = false; + return function(...args) { + if (called) return; + called = true; + return fn.apply(this, args); + }; +}; + const onUserInputChange = callback => { let type = 'mouse', lastTime = 0; @@ -800,14 +1003,7 @@ const onUserInputChange = callback => { (type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler); }); }; -const once = fn => { - let called = false; - return function(...args) { - if (called) return; - called = true; - return fn.apply(this, args); - }; -}; + const orderBy = (arr, props, orders) => [...arr].sort((a, b) => props.reduce((acc, prop, i) => { @@ -818,14 +1014,19 @@ const orderBy = (arr, props, orders) => return acc; }, 0) ); + const over = (...fns) => (...args) => fns.map(fn => fn.apply(null, args)); + const overArgs = (fn, transforms) => (...args) => fn(...args.map((val, i) => transforms[i](val))); + const pad = (str, length, char = ' ') => str.padStart((str.length + length) / 2, char).padEnd(length, char); + const palindrome = str => { const s = str.toLowerCase().replace(/[\W_]/g, ''); return s === [...s].reverse().join(''); }; + const parseCookie = str => str .split(';') @@ -834,8 +1035,11 @@ const parseCookie = str => acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim()); return acc; }, {}); + const partial = (fn, ...partials) => (...args) => fn(...partials, ...args); + const partialRight = (fn, ...partials) => (...args) => fn(...args, ...partials); + const partition = (arr, fn) => arr.reduce( (acc, val, i, arr) => { @@ -844,8 +1048,10 @@ const partition = (arr, fn) => }, [[], []] ); + const percentile = (arr, val) => (100 * arr.reduce((acc, v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0)) / arr.length; + const permutations = arr => { if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr; return arr.reduce( @@ -856,21 +1062,28 @@ const permutations = arr => { [] ); }; + const pick = (obj, arr) => arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {}); + const pickBy = (obj, fn) => Object.keys(obj) .filter(k => fn(obj[k], k)) .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); + const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg)); + const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args))); + const pluralize = (val, word, plural = word + 's') => { const _pluralize = (num, word, plural = word + 's') => [1, -1].includes(Number(num)) ? word : plural; if (typeof val === 'object') return (num, word) => _pluralize(num, word, val[word]); return _pluralize(val, word, plural); }; + const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]); + const prefix = prop => { const capitalizedProp = prop.charAt(0).toUpperCase() + prop.slice(1); const prefixes = ['', 'webkit', 'moz', 'ms', 'o']; @@ -879,6 +1092,7 @@ const prefix = prop => { ); return i !== -1 ? (i === 0 ? prop : prefixes[i] + capitalizedProp) : null; }; + const prettyBytes = (num, precision = 3, addSpace = true) => { const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0]; @@ -886,6 +1100,7 @@ const prettyBytes = (num, precision = 3, addSpace = true) => { const n = Number(((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision)); return (num < 0 ? '-' : '') + n + (addSpace ? ' ' : '') + UNITS[exponent]; }; + const primes = num => { let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2), sqroot = Math.floor(Math.sqrt(num)), @@ -893,16 +1108,19 @@ const primes = num => { numsTillSqroot.forEach(x => (arr = arr.filter(y => y % x !== 0 || y === x))); return arr; }; + const promisify = func => (...args) => new Promise((resolve, reject) => func(...args, (err, result) => (err ? reject(err) : resolve(result))) ); + const pull = (arr, ...args) => { let argState = Array.isArray(args[0]) ? args[0] : args; let pulled = arr.filter((v, i) => !argState.includes(v)); arr.length = 0; pulled.forEach(v => arr.push(v)); }; + const pullAtIndex = (arr, pullArr) => { let removed = []; let pulled = arr @@ -912,6 +1130,7 @@ const pullAtIndex = (arr, pullArr) => { pulled.forEach(v => arr.push(v)); return removed; }; + const pullAtValue = (arr, pullArr) => { let removed = [], pushToRemove = arr.forEach((v, i) => (pullArr.includes(v) ? removed.push(v) : v)), @@ -920,6 +1139,7 @@ const pullAtValue = (arr, pullArr) => { mutateTo.forEach(v => arr.push(v)); return removed; }; + const pullBy = (arr, ...args) => { const length = args.length; let fn = length > 1 ? args[length - 1] : undefined; @@ -929,22 +1149,30 @@ const pullBy = (arr, ...args) => { arr.length = 0; pulled.forEach(v => arr.push(v)); }; + const radsToDegrees = rad => (rad * 180.0) / Math.PI; + const randomHexColorCode = () => { let n = (Math.random() * 0xfffff * 1000000).toString(16); return '#' + n.slice(0, 6); }; + const randomIntArrayInRange = (min, max, n = 1) => Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min); + const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; + const randomNumberInRange = (min, max) => Math.random() * (max - min) + min; + const readFileLines = filename => fs .readFileSync(filename) .toString('UTF8') .split('\n'); + const rearg = (fn, indexes) => (...args) => fn(...indexes.map(i => args[i])); + const recordAnimationFrames = (callback, autoStart = true) => { let running = true, raf; @@ -965,12 +1193,10 @@ const recordAnimationFrames = (callback, autoStart = true) => { if (autoStart) start(); return { start, stop }; }; + const redirect = (url, asLink = true) => asLink ? (window.location.href = url) : window.location.replace(url); -const reduceSuccessive = (arr, fn, acc) => - arr.reduce((res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res), [acc]); -const reduceWhich = (arr, comparator = (a, b) => a - b) => - arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a)); + const reducedFilter = (data, keys, fn) => data.filter(fn).map(el => keys.reduce((acc, key) => { @@ -978,7 +1204,15 @@ const reducedFilter = (data, keys, fn) => return acc; }, {}) ); + +const reduceSuccessive = (arr, fn, acc) => + arr.reduce((res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res), [acc]); + +const reduceWhich = (arr, comparator = (a, b) => a - b) => + arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a)); + const reject = (pred, array) => array.filter((...args) => !pred(...args)); + const remove = (arr, func) => Array.isArray(arr) ? arr.filter(func).reduce((acc, val) => { @@ -986,7 +1220,9 @@ const remove = (arr, func) => return acc.concat(val); }, []) : []; + const removeNonASCII = str => str.replace(/[^\x20-\x7E]/g, ''); + const renameKeys = (keysMap, obj) => Object.keys(obj).reduce( (acc, key) => ({ @@ -995,8 +1231,13 @@ const renameKeys = (keysMap, obj) => }), {} ); + const reverseString = str => [...str].reverse().join(''); + +const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); + const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`); + const runAsync = fn => { const worker = new Worker( URL.createObjectURL(new Blob([`postMessage((${fn})());`]), { @@ -1012,8 +1253,11 @@ const runAsync = fn => { }; }); }; + const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve()); + const sample = arr => arr[Math.floor(Math.random() * arr.length)]; + const sampleSize = ([...arr], n = 1) => { let m = arr.length; while (m) { @@ -1022,6 +1266,7 @@ const sampleSize = ([...arr], n = 1) => { } return arr.slice(0, n); }; + const scrollToTop = () => { const c = document.documentElement.scrollTop || document.body.scrollTop; if (c > 0) { @@ -1029,6 +1274,7 @@ const scrollToTop = () => { window.scrollTo(0, c - c / 8); } }; + const sdbm = str => { let arr = str.split(''); return arr.reduce( @@ -1037,15 +1283,21 @@ const sdbm = str => { 0 ); }; + const serializeCookie = (name, val) => `${encodeURIComponent(name)}=${encodeURIComponent(val)}`; + const setStyle = (el, ruleName, val) => (el.style[ruleName] = val); + const shallowClone = obj => Object.assign({}, obj); + const shank = (arr, index = 0, delCount = 0, ...elements) => arr .slice(0, index) .concat(elements) .concat(arr.slice(index + delCount)); + const show = (...el) => [...el].forEach(e => (e.style.display = '')); + const shuffle = ([...arr]) => { let m = arr.length; while (m) { @@ -1054,7 +1306,9 @@ const shuffle = ([...arr]) => { } return arr; }; + const similarity = (arr, values) => arr.filter(v => values.includes(v)); + const size = val => Array.isArray(val) ? val.length @@ -1063,28 +1317,35 @@ const size = val => : typeof val === 'string' ? new Blob([val]).size : 0; + const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + const smoothScroll = element => document.querySelector(element).scrollIntoView({ behavior: 'smooth' }); + const sortCharactersInString = str => [...str].sort((a, b) => a.localeCompare(b)).join(''); + const sortedIndex = (arr, n) => { const isDescending = arr[0] > arr[arr.length - 1]; const index = arr.findIndex(el => (isDescending ? n >= el : n <= el)); return index === -1 ? arr.length : index; }; + const sortedIndexBy = (arr, n, fn) => { const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); const val = fn(n); const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el))); return index === -1 ? arr.length : index; }; + const sortedLastIndex = (arr, n) => { const isDescending = arr[0] > arr[arr.length - 1]; const index = arr.reverse().findIndex(el => (isDescending ? n <= el : n >= el)); return index === -1 ? 0 : arr.length - index; }; + const sortedLastIndexBy = (arr, n, fn) => { const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); const val = fn(n); @@ -1094,13 +1355,16 @@ const sortedLastIndexBy = (arr, n, fn) => { .findIndex(el => (isDescending ? val <= el : val >= el)); return index === -1 ? 0 : arr.length - index; }; + const splitLines = str => str.split(/\r?\n/); const spreadOver = fn => argsArr => fn(...argsArr); + const stableSort = (arr, compare) => arr .map((item, index) => ({ item, index })) .sort((a, b) => compare(a.item, b.item) || a.index - b.index) .map(({ item }) => item); + const standardDeviation = (arr, usePopulation = false) => { const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length; return Math.sqrt( @@ -1108,6 +1372,7 @@ const standardDeviation = (arr, usePopulation = false) => { (arr.length - (usePopulation ? 0 : 1)) ); }; + const stringPermutations = str => { if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str]; return str @@ -1118,38 +1383,51 @@ const stringPermutations = str => { [] ); }; + const stripHTMLTags = str => str.replace(/<[^>]*>/g, ''); + const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0); + const sumBy = (arr, fn) => arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0); + const sumPower = (end, power = 2, start = 1) => Array(end + 1 - start) .fill(0) .map((x, i) => (i + start) ** power) .reduce((a, b) => a + b, 0); + const symmetricDifference = (a, b) => { const sA = new Set(a), sB = new Set(b); return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))]; }; + const symmetricDifferenceBy = (a, b, fn) => { const sA = new Set(a.map(v => fn(v))), sB = new Set(b.map(v => fn(v))); return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))]; }; + const symmetricDifferenceWith = (arr, val, comp) => [ ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1), ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1) ]; + const tail = arr => (arr.length > 1 ? arr.slice(1) : arr); + const take = (arr, n = 1) => arr.slice(0, n); + const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length); + const takeRightWhile = (arr, func) => arr.reduceRight((acc, el) => (func(el) ? acc : [el, ...acc]), []); + const takeWhile = (arr, func) => { for (const [i, val] of arr.entries()) if (func(val)) return arr.slice(0, i); return arr; }; + const throttle = (fn, wait) => { let inThrottle, lastFn, lastTime; return function() { @@ -1170,16 +1448,19 @@ const throttle = (fn, wait) => { } }; }; + +const times = (n, fn, context = undefined) => { + let i = 0; + while (fn.call(context, i) !== false && ++i < n) {} +}; + const timeTaken = callback => { console.time('timeTaken'); const r = callback(); console.timeEnd('timeTaken'); return r; }; -const times = (n, fn, context = undefined) => { - let i = 0; - while (fn.call(context, i) !== false && ++i < n) {} -}; + const toCamelCase = str => { let s = str && @@ -1189,21 +1470,37 @@ const toCamelCase = str => { .join(''); return s.slice(0, 1).toLowerCase() + s.slice(1); }; + const toCurrency = (n, curr, LanguageFormat = undefined) => Intl.NumberFormat(LanguageFormat, { style: 'currency', currency: curr }).format(n); + const toDecimalMark = num => num.toLocaleString('en-US'); + +const toggleClass = (el, className) => el.classList.toggle(className); + const toHash = (object, key) => Array.prototype.reduce.call( object, (acc, data, index) => ((acc[!key ? index : data[key]] = data), acc), {} ); + const toKebabCase = str => str && str .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) .map(x => x.toLowerCase()) .join('-'); + +const tomorrow = (long = false) => { + let t = new Date(); + t.setDate(t.getDate() + 1); + const ret = `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, '0')}-${String( + t.getDate() + ).padStart(2, '0')}`; + return !long ? ret : `${ret}T00:00:00`; +}; + const toOrdinalSuffix = num => { const int = parseInt(num), digits = [int % 10, int % 100], @@ -1214,14 +1511,17 @@ const toOrdinalSuffix = num => { ? int + ordinals[digits[0] - 1] : int + ordinals[3]; }; + const toSafeInteger = num => Math.round(Math.max(Math.min(num, Number.MAX_SAFE_INTEGER), Number.MIN_SAFE_INTEGER)); + const toSnakeCase = str => str && str .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) .map(x => x.toLowerCase()) .join('_'); + const toTitleCase = str => str .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) @@ -1234,17 +1534,23 @@ const tomorrow = () => { return t.toISOString().split('T')[0]; }; const transform = (obj, fn, acc) => Object.keys(obj).reduce((a, k) => fn(a, obj[k], k, obj), acc); + const triggerEvent = (el, eventType, detail) => el.dispatchEvent(new CustomEvent(eventType, { detail })); + const truncateString = (str, num) => str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str; + const truthCheckCollection = (collection, pre) => collection.every(obj => obj[pre]); + const unary = fn => val => fn(val); + const uncurry = (fn, n = 1) => (...args) => { const next = acc => args => args.reduce((x, y) => x(y), acc); if (n > args.length) throw new RangeError('Arguments too few!'); return next(fn)(args.slice(0, n)); }; + const unescapeHTML = str => str.replace( /&|<|>|'|"/g, @@ -1257,6 +1563,7 @@ const unescapeHTML = str => '"': '"' }[tag] || tag) ); + const unflattenObject = obj => Object.keys(obj).reduce((acc, k) => { if (k.indexOf('.') !== -1) { @@ -1273,34 +1580,44 @@ const unflattenObject = obj => } else acc[k] = obj[k]; return acc; }, {}); + const unfold = (fn, seed) => { let result = [], val = [null, seed]; while ((val = fn(val[1]))) result.push(val[0]); return result; }; + const union = (a, b) => Array.from(new Set([...a, ...b])); + const unionBy = (a, b, fn) => { const s = new Set(a.map(fn)); return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))])); }; + const unionWith = (a, b, comp) => Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)])); + const uniqueElements = arr => [...new Set(arr)]; + const uniqueElementsBy = (arr, fn) => arr.reduce((acc, v) => { if (!acc.some(x => fn(v, x))) acc.push(v); return acc; }, []); + const uniqueElementsByRight = (arr, fn) => arr.reduceRight((acc, v) => { if (!acc.some(x => fn(v, x))) acc.push(v); return acc; }, []); + const uniqueSymmetricDifference = (a, b) => [ ...new Set([...a.filter(v => !b.includes(v)), ...b.filter(v => !a.includes(v))]) ]; + const untildify = str => str.replace(/^~($|\/|\\)/, `${require('os').homedir()}$1`); + const unzip = arr => arr.reduce( (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc), @@ -1308,6 +1625,7 @@ const unzip = arr => length: Math.max(...arr.map(x => x.length)) }).map(x => []) ); + const unzipWith = (arr, fn) => arr .reduce( @@ -1317,21 +1635,51 @@ const unzipWith = (arr, fn) => }).map(x => []) ) .map(val => fn(...val)); + +const URLJoin = (...args) => + args + .join('/') + .replace(/[\/]+/g, '/') + .replace(/^(.+):\//, '$1://') + .replace(/^file:/, 'file:/') + .replace(/\/(\?|&|#[^!])/g, '$1') + .replace(/\?/g, '&') + .replace('&', '?'); + +const UUIDGeneratorBrowser = () => + ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => + (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16) + ); + + +const UUIDGeneratorNode = () => + ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => + (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16) + ); + const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n; + const when = (pred, whenTrue) => x => (pred(x) ? whenTrue(x) : x); + const without = (arr, ...args) => arr.filter(v => !args.includes(v)); + const words = (str, pattern = /[^a-zA-Z-]+/) => str.split(pattern).filter(Boolean); + const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []); + const yesNo = (val, def = false) => /^(y|yes)$/i.test(val) ? true : /^(n|no)$/i.test(val) ? false : def; + const zip = (...arrays) => { const maxLength = Math.max(...arrays.map(x => x.length)); return Array.from({ length: maxLength }).map((_, i) => { return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]); }); }; + const zipObject = (props, values) => props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {}); + const zipWith = (...array) => { const fn = typeof array[array.length - 1] === 'function' ? array.pop() : undefined; return Array.from( @@ -1339,10 +1687,7 @@ const zipWith = (...array) => { (_, i) => (fn ? fn(...array.map(a => a[i])) : array.map(a => a[i])) ); }; -const JSONToDate = arr => { - const dt = new Date(parseInt(arr.toString().substr(6))); - return `${dt.getDate()}/${dt.getMonth() + 1}/${dt.getFullYear()}`; -}; + const binarySearch = (arr, val, start = 0, end = arr.length - 1) => { if (start > end) return -1; const mid = Math.floor((start + end) / 2); @@ -1350,7 +1695,9 @@ const binarySearch = (arr, val, start = 0, end = arr.length - 1) => { if (arr[mid] < val) return binarySearch(arr, val, mid + 1, end); return mid; }; + const celsiusToFahrenheit = degrees => 1.8 * degrees + 32; + const cleanObj = (obj, keysToKeep = [], childIndicator) => { Object.keys(obj).forEach(key => { if (key === childIndicator) { @@ -1361,8 +1708,11 @@ const cleanObj = (obj, keysToKeep = [], childIndicator) => { }); return obj; }; + const collatz = n => (n % 2 === 0 ? n / 2 : 3 * n + 1); + const countVowels = str => (str.match(/[aeiou]/gi) || []).length; + const factors = (num, primes = false) => { const isPrime = num => { const boundary = Math.floor(Math.sqrt(num)); @@ -1382,9 +1732,12 @@ const factors = (num, primes = false) => { }, []); return primes ? array.filter(isPrime) : array; }; + const fahrenheitToCelsius = degrees => (degrees - 32) * 5/9; + const fibonacciCountUntilNum = num => Math.ceil(Math.log(num * Math.sqrt(5) + 1 / 2) / Math.log((Math.sqrt(5) + 1) / 2)); + const fibonacciUntilNum = num => { let n = Math.ceil(Math.log(num * Math.sqrt(5) + 1 / 2) / Math.log((Math.sqrt(5) + 1) / 2)); return Array.from({ length: n }).reduce( @@ -1392,10 +1745,12 @@ const fibonacciUntilNum = num => { [] ); }; + const heronArea = (side_a, side_b, side_c) => { const p = (side_a + side_b + side_c) / 2 return Math.sqrt(p * (p-side_a) * (p-side_b) * (p-side_c)) }; + const howManyTimes = (num, divisor) => { if (divisor === 1 || divisor === -1) return Infinity; if (divisor === 0) return 0; @@ -1406,6 +1761,7 @@ const howManyTimes = (num, divisor) => { } return i; }; + const httpDelete = (url, callback, err = console.error) => { const request = new XMLHttpRequest(); request.open('DELETE', url, true); @@ -1413,6 +1769,7 @@ const httpDelete = (url, callback, err = console.error) => { request.onerror = () => err(request); request.send(); }; + const httpPut = (url, data, callback, err = console.error) => { const request = new XMLHttpRequest(); request.open("PUT", url, true); @@ -1421,10 +1778,12 @@ const httpPut = (url, data, callback, err = console.error) => { request.onerror = () => err(request); request.send(data); }; + const isArmstrongNumber = digits => (arr => arr.reduce((a, d) => a + parseInt(d) ** arr.length, 0) == digits)( (digits + '').split('') ); + const isSimilar = (pattern, str) => [...str].reduce( (matchIndex, char) => @@ -1433,7 +1792,14 @@ const isSimilar = (pattern, str) => : matchIndex, 0 ) === pattern.length; + +const JSONToDate = arr => { + const dt = new Date(parseInt(arr.toString().substr(6))); + return `${dt.getDate()}/${dt.getMonth() + 1}/${dt.getFullYear()}`; +}; + const kmphToMph = (kmph) => 0.621371192 * kmph; + const levenshteinDistance = (string1, string2) => { if (string1.length === 0) return string2.length; if (string2.length === 0) return string1.length; @@ -1458,8 +1824,11 @@ const levenshteinDistance = (string1, string2) => { } return matrix[string2.length][string1.length]; }; + const mphToKmph = (mph) => 1.6093440006146922 * mph; + const pipeLog = data => console.log(data) || data; + const quickSort = ([n, ...nums], desc) => isNaN(n) ? [] @@ -1468,7 +1837,9 @@ const quickSort = ([n, ...nums], desc) => n, ...quickSort(nums.filter(v => (!desc ? v > n : v <= n)), desc) ]; + const removeVowels = (str, repl = '') => str.replace(/[aeiou]/gi, repl); + const solveRPN = rpn => { const OPERATORS = { '*': (a, b) => a * b, @@ -1497,11 +1868,13 @@ const solveRPN = rpn => { if (stack.length === 1) return stack.pop(); else throw `${rpn} is not a proper RPN. Please check it and try again`; }; + const speechSynthesis = message => { const msg = new SpeechSynthesisUtterance(message); msg.voice = window.speechSynthesis.getVoices()[0]; window.speechSynthesis.speak(msg); }; + const squareSum = (...args) => args.reduce((squareSum, number) => squareSum + Math.pow(number, 2), 0); diff --git a/test/createDirIfNotExists.test.js b/test/createDirIfNotExists.test.js new file mode 100644 index 000000000..3c7fcb184 --- /dev/null +++ b/test/createDirIfNotExists.test.js @@ -0,0 +1,6 @@ +const expect = require('expect'); +const {createDirIfNotExists} = require('./_30s.js'); + +test('createDirIfNotExists is a Function', () => { + expect(createDirIfNotExists).toBeInstanceOf(Function); +});