UNPKG

aid.js

Version:

A Bundle of Javascript util Library to help developers. No dependency to other Libraries.

1,757 lines (1,480 loc) 158 kB
/* * aid.js 0.1.96 * https://www.npmjs.com/package/aid.js * * The MIT License (MIT) * Copyright (c) 2016-2019 Hyun-Seok.Kim, dragmove@gmail.com */ (function() { 'use strict'; // Establish the global object, `window` (`self`) in the browser, `global` // on the server, or `this` in some virtual machines. We use `self` // instead of `window` for `WebWorker` support. var global = (typeof self == 'object' && self.self === self && self) || (typeof global == 'object' && global.global === global && global) || this || {}; var _slice = Array.prototype.slice; var _hasOwnProperty = Object.prototype.hasOwnProperty; var aid = {}, operator = {}, platform = {}, browser = {}, string = {}, math = {}, date = {}, array = {}, object = {}, element = {}, file = {}, clipboard = {}, monad = {}; // https://en.wikipedia.org/wiki/Monad_(functional_programming) /** * is null/undefined or other * * @static * @method existy * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.existy(undefined) ); // false * console.log( aid.existy(null) ); // false * console.log( aid.existy('') ); // true */ aid.existy = function existy(any) { return any != null; }; /** * check defined * * @static * @method isDefined * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isDefined(undefined) ); // false * console.log( aid.isDefined(null) ); // false * console.log( aid.isDefined(0) ); // true */ aid.isDefined = function isDefined(any) { if (any === null || typeof any === 'undefined') return false; return true; }; /** * check type is Boolean * * @static * @method isBoolean * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isBoolean(false) ); // true */ aid.isBoolean = function isBoolean(any) { if (!aid.isDefined(any)) return false; return any.constructor === Boolean; }; /** * check type is Number * * @static * @method isNumber * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isNumber(-1) ); // true */ aid.isNumber = function isNumber(any) { if (!aid.isDefined(any)) return false; return !isNaN(any) && any.constructor === Number; }; /** * check type is Integer Number * * @static * @method isInteger * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isInteger(-1) ); // true */ aid.isInteger = function isInteger(any) { if (!aid.isNumber(any)) return false; // https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger return isFinite(any) && Math.floor(any) === any; }; /** * check type is String * * @static * @method isString * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isString('') ); // true */ aid.isString = function isString(any) { if (!aid.isDefined(any)) return false; return any.constructor === String; }; /** * check type is Array * * @static * @method isArray * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isArray([]) ); // true */ aid.isArray = function isArray(any) { if (!aid.isDefined(any)) return false; return any.constructor === Array; }; /** * check type is Object * * @static * @method isObject * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isObject({}) ); // true */ aid.isObject = function isObject(any) { if (!aid.isDefined(any)) return false; return any.constructor === Object; }; /** * check type is Function * * @static * @method isFunction * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isFunction(function(){}) ); // true */ aid.isFunction = function isFunction(any) { if (!aid.isDefined(any)) return false; return any.constructor === Function; }; /** * check type is RegExp * * @static * @method isRegExp * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isRegExp(new RegExp('^aid')) ); // true * console.log( aid.isRegExp(/^aid/) ); // true */ aid.isRegExp = function isRegExp(any) { if (!aid.isDefined(any)) return false; return any.constructor === RegExp; }; /** * check type is Error (can check Error, EvalError, InternalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError) * refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error * * @static * @method isError * @param {*} any * @param {Constructor} errorType (optional) * @returns {Boolean} return boolean * @example * console.log( aid.isError(new Error('msg')) ); // true * console.log( aid.isError(new TypeError('msg')) ); // true * console.log( aid.isError(new RangeError('msg')) ); // true * console.log( aid.isError(new TypeError('msg'), TypeError) ); // true * console.log( aid.isError(new TypeError('msg'), Error) ); // false * console.log( aid.isError(new TypeError('msg'), RangeError) ); // false */ aid.isError = function isError(any, errorType) { if (!aid.isDefined(any)) return false; var con = any.constructor; if (!aid.isDefined(errorType)) { // Non-standard - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/InternalError if (window.InternalError && con === window.InternalError) return true; return ( con === Error || con === EvalError || con === RangeError || con === ReferenceError || con === SyntaxError || con === TypeError || con === URIError ); } return con === errorType; }; /** * check type is HTML element * refer to https://developer.mozilla.org/en-US/docs/Web/API/Element * * @static * @method isElement * @param {*} any * @returns {Boolean} return boolean * @example * console.log( aid.isElement(document.createElement('div')) ); // true */ aid.isElement = function isElement(any) { return aid.isDefined(any) && typeof any === 'object' && any.nodeType === 1 && any instanceof Node; }; /** * extend function * * @static * @method extend * @param {Object} prototype of class function, or object has variables or methods. * @param {Object} object has variables or methods * @example * function Destination() { * this.name = 'destination'; * this.type = 'json'; * }; * * var source = { * getName: function() { return this.name; }, * getType: function() { return this.type; } * }; * * aid.extend(Destination.prototype, source); * * var destination = new Destination(); * console.log( destination.getName() ); // 'destination' * console.log( destination.getType() ); // 'json' */ aid.extend = function extend(destination, source) { if (!(destination instanceof Object) || !(typeof destination === 'object')) { throw new TypeError('[aid.extend] Type of destination parameter must be instance of Object, and object.'); } if (!(typeof source === 'object')) throw new TypeError('[aid.extend] Type of source parameter must be Object.'); for (var key in source) { if (source.hasOwnProperty(key)) { destination[key] = source[key]; } } return destination; }; /** * inherit Class function * * @static * @method inherit * @param {class} ChildClass - child class function * @param {class} ParentClass - parent class function * @example */ aid.inherit = (function() { // use closure, protect gabarge collection. var F = function() {}; return function inherit(ChildClass, ParentClass) { F.prototype = ParentClass.prototype; ChildClass.prototype = new F(); ChildClass.prototype.constructor = ChildClass; ChildClass.super = ParentClass.prototype; }; })(); /** * create namespace * * @static * @method namespace * @param {String} namespace * @param {Object} parent * @returns {Object} return object * @example * aid.namespace('first.second.third'); // create first.second.third object */ aid.namespace = function(namespace, parent) { if (!aid.isString(namespace)) throw new TypeError('[aid.namespace] Type of namespace parameter must be String.'); if (!(aid.isObject(parent) || !aid.isDefined(parent))) { throw new TypeError('[aid.namespace] Type of parent parameter must be Object or null or undefined.'); } var ns = parent || global; if (namespace) { var parts = namespace.split('.'); parts.forEach(function(part) { if (!ns[part]) ns[part] = {}; ns = ns[part]; }); } return ns; }; /** * return memoized function * * @static * @method memoize * @param {Function} func * @param {Function} hasher (optional) * @returns {Function} return function * @example * var plus = function(a, b) { return a + b; }; * var memoized = aid.memoize(plus); * console.log( memoized('aid', '.js') ); // aid.js */ aid.memoize = function memoize(func, hasher) { if (!aid.isFunction(func)) throw new TypeError('[aid.memoize] Type of func parameter must be Function.'); if (aid.isDefined(hasher) && !aid.isFunction(hasher)) throw new TypeError('[aid.memoize] Type of hasher parameter must be undefined or null or Function.'); var memoized = function(_key) { var cache = memoized.cache; var key = hasher ? hasher.apply(null, _slice.call(arguments)) : _key; if (cache.has(key)) return cache.get(key); var result = func.apply(null, _slice.call(arguments)); cache.set(key, result); return result; }; memoized.cache = aid.createDictionary(); return memoized; }; /** * borrow method from donor object * * @static * @method borrow * @param {Object} borrower * @param {Object} donor * @param {String} funcName * @example * var borrower = {}, donor = { say: function() { return 'hello, world'; } }; * aid.borrow(borrower, donor, 'say'); * console.log( borrower.say() ); // 'hello, world' */ aid.borrow = function borrow(borrower, donor, funcName) { if (!aid.isObject(borrower) || !aid.isObject(donor)) throw new TypeError('[aid.borrow] Type of borrower, donor parameters must be Object.'); if (!aid.isString(funcName)) throw new TypeError('[aid.borrow] Type of funcName parameter must be String.'); if (!aid.isDefined(donor[funcName])) throw new Error('[aid.borrow] donor object parameter has not function with funcName parameter.'); if (aid.isDefined(borrower[funcName])) throw new Error('[aid.borrow] borrower object parameter already has function with funcName parameter.'); borrower[funcName] = function() { var args = _slice.call(arguments); return donor[funcName].apply(this, args); }; }; /** * return function bind context and parameters * * @static * @method bind * @param {Function} func * @param {Object} context * @returns {Function} return function * @example * this.title = 'global - aid.js'; * var obj = { title: 'obj - aid.js', getTitle: function() { return this.title; } }; * var getObjTitle = bind(obj.getTitle, obj), getGlobalTitle = bind(obj.getTitle, window); * console.log( getObjTitle() ); // 'obj - aid.js'; * console.log( getGlobalTitle() ); // 'global - aid.js'; */ aid.bind = function bind(func, context) { if (!aid.isFunction(func)) throw new TypeError('[aid.bind] Type of func parameter must be Function.'); return function() { return func.apply(context, arguments); }; }; /** * return function composed function_a and function_b * * @static * @method compose * @param {Function} func_a * @param {Function} func_b * @returns {Function} return function * @example * var isNotNaN = aid.compose(aid.operator['!'], isNaN); * console.log( isNotNaN(0) ); // true */ aid.compose = function compose(func_a, func_b) { if (!aid.isFunction(func_a) || !aid.isFunction(func_b)) { throw new TypeError('[aid.compose] Type of func_a, func_b parameters must be Function.'); } return function() { return func_a(func_b.apply(null, arguments)); }; }; /** * return function negate object parameter * * @static * @method not * @param {Function} func * @returns {Function} return function * @example * var isNotNaN = aid.not(isNaN); * console.log( isNotNaN(0) ); // true */ aid.not = function not(func) { if (!aid.isFunction(func)) throw new TypeError('[aid.not] Type of func parameter must be Function.'); return function() { return !func.apply(null, arguments); }; }; /** * each method for data can loop * * @static * @method each * @param {(Array|String)} iterable * @param {Function} func * @param {Object} context * @example * var result = []; * aid.each('aid.js', function(v) { result.push(v); }, null); * console.log( result.join('') ); // 'aid.js' */ aid.each = function each(iterable, func, context) { if (!(aid.isArray(iterable) || aid.isString(iterable))) { throw new TypeError('[aid.each] Type of iterable parameter must be Array or String.'); } var _context = aid.existy(context) ? context : null; for (var i = 0, max = iterable.length; i < max; i++) { func.call(_context, iterable[i]); } }; /** * check object is truthy * * @static * @method truthy * @param {Object} obj * @returns {Boolean} return boolean * @example * console.log( aid.truthy(true) ); // true * console.log( aid.truthy([]) ); // true * console.log( aid.truthy(0) ); // false * console.log( aid.truthy('') ); // false */ aid.truthy = function truthy(obj) { return !!obj; }; /** * check object is falsy * * @static * @method falsy * @param {Object} obj * @returns {Boolean} return boolean * @example * console.log( aid.falsy(true) ); // false * console.log( aid.falsy([]) ); // false * console.log( aid.falsy(0) ); // true * console.log( aid.falsy('') ); // true */ aid.falsy = function falsy(obj) { return !!!obj; }; /** * get indexed value of array or string * * @static * @method nth * @param {Array|String} iterable * @param {Number} index * @example * console.log( aid.nth('string', 1) ); // 't' * console.log( aid.nth('string', -1) ); // null * console.log( aid.nth([0, 'str', true], 2) ); // true * console.log( aid.nth([0, 'str', true], 99) ); // null */ aid.nth = function nth(iterable, index) { if (!(aid.isArray(iterable) || aid.isString(iterable))) { throw new TypeError('[aid.nth] Type of iterable parameter must be Array or String.'); } if (!aid.isInteger(index)) throw new TypeError('[aid.nth] Type of index parameter must be Integer Number.'); return index < 0 || index > iterable.length - 1 ? null : iterable[index]; }; /** * check all arguments are true * * @static * @method allOf * @returns {Boolean} return boolean * @example * console.log( aid.allOf(true, true) ); // true * console.log( aid.allOf(true, false) ); // false */ aid.allOf = function allOf(/*args*/) { var args = _slice.call(arguments); return args.every(function(val) { return val === true; }); }; /** * check some argument is true * * @static * @method anyOf * @returns {Boolean} return boolean * @example * console.log( anyOf(true, false) ); // true * console.log( anyOf(false, false) ); // false */ aid.anyOf = function anyOf(/*args*/) { var args = _slice.call(arguments); return args.some(function(val) { return val === true; }); }; /** * return function always return constant value * http://underscorejs.org/#constant * * @static * @method constant * @param {Object} obj * @returns {Function} return function * @example * var obj = {name: 'aid.js'}; * console.log( aid.constant(obj)() === obj ); // true */ aid.constant = function constant(obj) { return function() { return obj; }; }; /** * return function pluck field of object, array, string * * @static * @method plucker * @param {(String|Number)} field of object, array, string * @returns {Function} return function * @example * var getTitle = aid.plucker('title'); * var obj = {title: 'aid.js', description: 'A bundle of Javascript util Library for help developers. No dependency to other Libraries.'}; * console.log( getTitle(obj) ); // 'aid.js' * * var getFirst = aid.plucker(0); * var str = 'aid.js'; * console.log( getFirst(str) ); // 'a' * * var getLength = aid.plucker('length'); * var arr = [1, 2, 3, 4, 5]; * console.log( getLength(arr) ); // 5 */ aid.plucker = function plucker(field) { if (!(aid.isString(field) || aid.isNumber(field))) { throw new TypeError('[aid.plucker] Type of field parameter must be String or Number.'); } return function(obj) { if (!(aid.isObject(obj) || aid.isArray(obj) || aid.isString(obj))) { throw new TypeError('[aid.plucker] Type of obj parameter must be Object or Array or String.'); } return obj[field]; }; }; /** * return best(optimized by condition function) value * * @static * @method best * @param {Function} condition function to find best value * @param {Array} array * @example * console.log( aid.best(function(x, y) { return x > y; }, [2, 4, 1, 5, 3]) ); // 5 */ aid.best = function best(conditionFunc, array) { if (!aid.isFunction(conditionFunc)) throw new TypeError('[aid.best] Type of conditionFunc parameter must be Function.'); if (!aid.isArray(array)) throw new TypeError('[aid.best] Type of array parameter must be Array.'); return array.reduce(function(previousValue, currentValue) { return conditionFunc(previousValue, currentValue) ? previousValue : currentValue; }); }; /** * return array has values filtered * * @static * @method iterateUntil * @param {Function} function return value * @param {Function} function has condition * @param {Object} initial value * @returns {Array} return array has values filtered * @example * console.log( aid.iterateUntil(function(n) { return n + n; }, function(n) { return n <= 1042 }, 1) ); // [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] */ aid.iterateUntil = function iterateUntil(calculateFunc, conditionFunc, initialValue) { if (!aid.isFunction(calculateFunc)) throw new TypeError('[aid.iterateUntil] Type of calculateFunc parameter must be Function.'); if (!aid.isFunction(conditionFunc)) throw new TypeError('[aid.iterateUntil] Type of conditionFunc parameter must be Function.'); var array = [], result = calculateFunc(initialValue); while (conditionFunc(result)) { array.push(result); result = calculateFunc(result); } return array; }; /** * curry function can use one parameter * * @static * @method curry * @param {Function} function * @returns {Function} return function * @example */ aid.curry = function curry(func) { if (!aid.isFunction(func)) throw new TypeError('[aid.curry] Type of func parameter must be Function.'); return function(arg) { return func(arg); }; }; /** * curry function can use two parameter * * @static * @method curry2 * @param {Function} function * @returns {Function} return function * @example * var parseBinaryStr = aid.curry2(parseInt)(2); * console.log( parseBinaryStr('111') ); // 7 */ aid.curry2 = function curry2(func) { if (!aid.isFunction(func)) throw new TypeError('[aid.curry2] Type of func parameter must be Function.'); return function(secondArg) { return function(firstArg) { return func(firstArg, secondArg); }; }; }; /** * curry function for variadic functions * * @static * @method curryAll * @param {Function} function * @param {Number} minimum number of arguments to curry * @returns {Function} return function * @example * console.log( curryAll(Math.max, 2)(1, 99) ); // 99 * console.log( curryAll(Math.max, 2)(1)(99) ); // 99 */ aid.curryAll = function curryAll(func, curryArgsNum) { if (!aid.isFunction(func)) throw new TypeError('[aid.curryAll] Type of func parameter must be Function.'); var arity = curryArgsNum || func.length; return function curried() { var args = _slice.call(arguments), context = this; return args.length >= arity ? func.apply(context, args) : function() { var rest = _slice.call(arguments); return curried.apply(context, args.concat(rest)); }; }; }; /** * reverse arguments of function * refer to https://github.com/getify/Functional-Light-JS/blob/master/manuscript/ch3.md/#reversing-arguments * * @static * @method reverseArgs * @param {Function} function * @returns {Function} return function * @example * var getReverseArgs = aid.reverseArgs(function() { return Array.prototype.slice.call(arguments); }); * console.log( getReverseArgs(99, 'aid.js') ); // ['aid.js', 99] */ aid.reverseArgs = function reverseArgs(func) { if (!aid.isFunction(func)) throw new TypeError('[aid.reverseArgs] Type of func parameter must be Function.'); return function(/* args... */) { var args = _slice.call(arguments); return func.apply(null, args.concat().reverse()); }; }; /** * partial application * * @static * @method partial * @param {Function} function * @returns {Function} return function * @example * function sum() { * var args = Array.prototype.slice.call(arguments); * return args.reduce(function(acc, cur) { * return acc + cur; * }, 0); * } * console.log( aid.partial(sum)() ); // 0 * console.log( aid.partial(sum, 1)() ); // 1 * console.log( aid.partial(sum, 1)(2) ); // 3 * console.log( aid.partial(sum)(1, 2, 3) ); // 6 */ aid.partial = function partial(func /*, args... */) { if (!aid.isFunction(func)) throw new TypeError('[aid.partial] Type of func parameter must be Function.'); var args = aid.rest(_slice.call(arguments)); return function(/* args... */) { return func.apply(func, args.concat(_slice.call(arguments))); }; }; /** * partial application for the right arguments * * @static * @method partialRight * @param {Function} function * @returns {Function} return function * @example * function three(str, num, arr) { return str + ' ' + num + ' ' + arr; } * console.log( aid.partialRight(three, 99, [1, 2, 3])('aid.js') ); // 'aid.js 99 1,2,3' * console.log( aid.partial( aid.partialRight(three, [1, 2, 3]), 'aid.js' )(99) ); // 'aid.js 99 1,2,3' */ aid.partialRight = function partialRight(func /*, args... */) { if (!aid.isFunction(func)) throw new TypeError('[aid.partialRight] Type of func parameter must be Function.'); var args = aid.rest(_slice.call(arguments)); return function(/* args... */) { return func.apply(null, _slice.call(arguments).concat(args)); }; }; /** * rest * refer to _.rest function of underscore.js - https://github.com/jashkenas/underscore/blob/master/underscore.js * * @static * @method rest * @param {Array} array * @param {Number} begin index to slice arguments. * @returns {Array} return array * @example * var array = [1, 2, 3, 4, 5]; * console.log( aid.rest(array) ); // [2, 3, 4, 5] * console.log( aid.rest(array, 2) ); // [3, 4, 5] */ aid.rest = function rest(array, beginIndex) { if (!aid.isArray(array)) throw new TypeError('[aid.rest] Type of array parameter must be Array.'); var begin = !aid.existy(beginIndex) ? 1 : beginIndex; return _slice.call(array, begin); }; /** * pipeline * * @static * @method pipeline * @param {Object} seed * @returns {Object} return value * @example * function negative(n) { return n * -1; } * function half(n) { return n / 2; } * function negativeHalf(n) { return aid.pipeline(n, negative, half); } * console.log( aid.pipeline(80, negative) ); // -80 * console.log( negativeHalf(80) ); // 80 * -1 / 2 */ aid.pipeline = function pipeline(seed /* args */) { var restArgs = aid.rest(_slice.call(arguments)); aid.each( restArgs, function(value) { if (!aid.isFunction(value)) throw new TypeError('[aid.pipeline] Type of rest parameters must be Function.'); }, null ); return restArgs.reduce(function(prev, current) { return current(prev); }, seed); }; /** * lazyChain * * @static * @method lazyChain * @param {Object} object * @returns {Object} return {invoke, force} * @example * var lazy = aid.lazyChange([2, 1, 3]).invoke('concat', [7, 7, 8, 9, 0]).invoke('sort'); * console.log( lazy.force() ); // [0, 1, 2, 3, 7, 7, 8, 9] * * // with aid.pipeline * function double(array) { return array.map(function(v) { return v * 2; }); } * function lazyReverseAndNegative(array) { return aid.lazyChain(array).invoke('reverse').invoke('map', function(v) { return v * -1; }); } * console.log( aid.pipeline([1, 2, 3], double, lazyReverseAndNegative).force() ); // [-6, -4, -2] */ aid.lazyChain = function lazyChain(obj) { var calls = []; return { invoke: function(methodName /*, args */) { var args = aid.rest(_slice.call(arguments)); calls.push(function(target) { var method = target[methodName]; if (!aid.isDefined(method)) { throw Error('[aid.lazyChain] ' + target.constructor.name + ' has not ' + methodName + ' method.'); } return method.apply(target, args); }); return this; }, force: function() { return calls.reduce(function(ret, thunk) { return thunk(ret); }, obj); }, }; }; /** * check two parameters are equal * * @static * @method eq * @param {Object} obj * @param {Object} obj * @returns {Boolean} return boolean * @example * console.log( aid.eq(99)(99) ); // true */ aid.eq = aid.curry2(function(lhs, rhs) { return lhs === rhs; }); /** * identity combinator * * @static * @method identity * @param {Object} value * @returns {Object} return value * @example * TODO: */ aid.identity = function identity(value) { return value; }; /** * tab combinator * * @static * @method tab * @param {Function} function * @returns {Function} return function * @example * TODO: */ aid.tab = function tab(func) { if (!aid.isFunction(func)) throw new TypeError('[aid.tab] Type of func parameter must be Function.'); return function(value) { func(value); return value; }; }; /** * alt combinator * * @static * @method alt * @param {Function} func_a * @param {Function} func_b * @returns {Function} return function * @example * var alt = aid.alt(function(val) { return val; }, function(val) { return !val; }); * console.log( alt(false) ); // true */ aid.alt = function alt(func_a, func_b) { if (!aid.isFunction(func_a) || !aid.isFunction(func_b)) { throw new TypeError('[aid.alt] Type of func_a, func_b parameters must be Function.'); } return function(value) { var result_a = func_a(value); // undefined, null, false if (!aid.isDefined(result_a) || result_a === false) return func_b(value); return result_a; }; }; /** * seq combinator * * @static * @method seq * @param {Function} functions * @returns {Function} return function * @example * var seq = aid.seq(function(val) { console.log(val); }, function(val) { console.log(val / -1) }); * seq(99); // 99, -99 */ aid.seq = function seq(/* functions */) { var funcs = _slice.call(arguments); funcs.forEach(function(func) { if (!aid.isFunction(func)) throw new TypeError('[aid.seq] Requires function parameters.'); }); return function(value) { funcs.forEach(function(func) { func.call(null, value); }); }; }; /** * fork(join) combinator * * @static * @method fork * @param {Function} join * @param {Function} func_a * @param {Function} func_b * @returns {Function} return function * @example * var fork = aid.fork(function(val_a, val_b) { return [val_a, val_b]; }, function(val) { return val; }, function(val) { return !val; }); * console.log( fork(true) ); // [true, false] */ aid.fork = function fork(join, func_a, func_b) { if (!aid.isFunction(join) || !aid.isFunction(func_a) || !aid.isFunction(func_b)) { throw new TypeError('[aid.fork] Type of join, func_a, func_b parameters must be Function.'); } return function(value) { return join.call(null, func_a.call(null, value), func_b.call(null, value)); }; }; /* * Data Structure */ // Stack var Stack = function() { this._dataStore = []; this._top = 0; }; Stack.prototype.push = function stack_push(element) { if (this._top < 0) this._top = 0; this._dataStore[this._top++] = element; }; Stack.prototype.pop = function stack_pop() { return this._dataStore[--this._top]; }; Stack.prototype.peek = function stack_peek() { return this._dataStore[this._top - 1]; }; Stack.prototype.length = function stack_length() { return this._top > 0 ? this._top : 0; }; Stack.prototype.clear = function stack_clear() { this._dataStore = []; this._top = 0; }; /** * createStack * * @static * @method createStack * @returns {Stack} return stack instance * @example * var stack = aid.createStack(); // use push, pop, peek, length, clear methods */ aid.createStack = function createStack() { return new Stack(); }; // Queue var Queue = function() { this._dataStore = []; }; Queue.prototype.enqueue = function queue_enqueue(element) { this._dataStore.push(element); }; Queue.prototype.dequeue = function queue_dequeue() { return this._dataStore.shift(); }; Queue.prototype.front = function queue_front() { return this._dataStore[0]; }; Queue.prototype.rear = function queue_rear() { return this._dataStore[this._dataStore.length - 1]; }; Queue.prototype.length = function queue_length() { return this._dataStore.length; }; Queue.prototype.isEmpty = function queue_isEmpty() { if (this._dataStore.length <= 0) return true; return false; }; /** * createQueue * * @static * @method createQueue * @returns {Queue} return queue instance * @example * var queue = aid.createQueue(); // use enqueue, dequeue, front, rear, length, isEmpty methods */ aid.createQueue = function createQueue() { return new Queue(); }; // LinkedList node var LinkedListNode = function(data) { this.data = data; this.next = null; }; // LinkedList var LinkedList = function() { this.head = new LinkedListNode('LinkedListHead_' + Date.now()); }; LinkedList.prototype.getHead = function linkedList_getHead() { return this.head; }; LinkedList.prototype.isEmpty = function linkedList_isEmpty() { return !this.head.next; }; LinkedList.prototype.find = function linkedList_find(data) { var node = this.head; while (node.data !== data) { node = node.next; if (!node) return node; } return node; }; LinkedList.prototype.findPrevious = function linkedList_findPrevious(data) { if (this.head.data === data) return null; var node = this.head; while (node.next && node.next.data !== data) { node = node.next; } return node; }; LinkedList.prototype.insert = function linkedList_insert(data, prevNodeData) { var prevNode = this.find(prevNodeData); if (!prevNode) return false; var insertNode = new LinkedListNode(data); insertNode.next = prevNode.next; prevNode.next = insertNode; return true; }; LinkedList.prototype.remove = function linkedList_remove(data) { var prevNode = this.findPrevious(data); if (prevNode && prevNode.next) { prevNode.next = prevNode.next.next; return true; } return false; }; LinkedList.prototype.append = function linkedList_append(data) { var appendNode = new LinkedListNode(data); var node = this.head; while (node.next) { node = node.next; } node.next = appendNode; }; LinkedList.prototype.getAllNodes = function linkedList_getAllNodes() { var nodes = [this.head], node = this.head; while (node.next) { nodes.push(node.next); node = node.next; } return nodes; }; /** * createLinkedList * * @static * @method createLinkedList * @returns {LinkedList} return linkedList instance * @example * var linkedList = aid.createLinkedList(); // use getHead, isEmpty, find, findPrevious, insert, remove, append, getAllNodes methods */ aid.createLinkedList = function createLinkedList() { return new LinkedList(); }; // Set var _Set = function() { this.store = []; }; _Set.prototype.size = function set_size() { return this.store.length; }; _Set.prototype.add = function set_add(value) { if (!this.has(value)) { this.store.push(value); return true; } return false; }; _Set.prototype.remove = function set_remove(value) { var pos = this.store.indexOf(value); if (pos >= 0) { this.store.splice(pos, 1); return true; } return false; }; _Set.prototype.clear = function set_clear() { this.store = []; }; _Set.prototype.has = function set_has(value) { return this.store.indexOf(value) >= 0; }; _Set.prototype.values = function set_values() { return this.store; }; // A ∪ B _Set.prototype.union = function set_union(otherSet) { var unionSet = new _Set(); this.values().forEach(function(val) { unionSet.add(val); }); otherSet.values().forEach(function(val) { unionSet.add(val); }); return unionSet; }; // A ∩ B _Set.prototype.intersection = function set_intersection(otherSet) { var intersectionSet = new _Set(); this.values().forEach(function(val) { if (otherSet.has(val)) intersectionSet.add(val); }); return intersectionSet; }; // A - B _Set.prototype.difference = function set_difference(otherSet) { var differenceSet = new _Set(); this.values().forEach(function(val) { if (!otherSet.has(val)) differenceSet.add(val); }); return differenceSet; }; // A ⊆ B _Set.prototype.isSubset = function set_subset(superset) { if (this.size() > superset.size()) return false; var isSupersetContainsSubset = this.values().every(function(val) { return superset.has(val); }); return isSupersetContainsSubset; }; /** * createSet * * @static * @method createSet * @returns {Set} return set instance * @example * var set = aid.createSet(); // use size, add, remove, clear, has, values, union, intersection, difference, isSubset methods */ aid.createSet = function createSet() { return new _Set(); }; // HashTable var HashTable = function(hashFunc) { this.table = []; this._hashFunc = aid.isFunction(hashFunc) ? hashFunc : this._djb2Hash; }; HashTable.prototype.put = function hashTable_put(key, value) { var position = this._hashFunc.call(null, key); if (this.table[position] === undefined) this.table[position] = aid.createLinkedList(); var linkedList = this.table[position]; linkedList.append({ key: key, value: value }); }; HashTable.prototype.get = function hashTable_get(key) { var position = this._hashFunc.call(null, key); var linkedList = this.table[position]; if (linkedList) { var head = linkedList.getHead(); if (!head.next) return undefined; var node = head; while (node && node.data && node.data.key !== key) { node = node.next; } if (node && node.data) return node.data.value; } return undefined; }; HashTable.prototype.remove = function hashTable_remove(key) { var position = this._hashFunc.call(null, key); var linkedList = this.table[position]; if (linkedList) { var head = linkedList.getHead(); if (!head.next) return false; // find previous node var node = head; while (node.next && node.next.data && node.next.data.key !== key) { node = node.next; } // remove node if (node && node.next) { node.next = node.next.next; if (linkedList.isEmpty()) this.table[position] = undefined; return true; } } return false; }; HashTable.prototype._djb2Hash = function hashTable_djb2Hash(key) { var hash = 5381; for (var i = 0, max = key.length; i < max; i++) { hash = hash * 33 + key.charCodeAt(i); } return hash % 1013; }; /** * createHashTable * * @static * @method createQueue * @returns {Queue} return hashTable instance that use djb2 hash function. * @example * var HashTable = aid.createHashTable(); // use put, get, remove methods */ aid.createHashTable = function createHashTable(hashFunc) { return new HashTable(hashFunc); }; // Dictionary function Dictionary() { this.items = {}; } Dictionary.prototype.has = function(key) { return _hasOwnProperty.call(this.items, key); }; Dictionary.prototype.get = function(key) { return this.has(key) ? this.items[key] : undefined; }; Dictionary.prototype.set = function(key, value) { this.items[key] = value; }; Dictionary.prototype.remove = function(key) { if (this.has(key)) { delete this.items[key]; return true; } return false; }; Dictionary.prototype.clear = function() { this.items = {}; }; Dictionary.prototype.keys = function() { return Object.keys(this.items); }; Dictionary.prototype.values = function() { var values = []; for (var key in this.items) { if (this.has(key)) values.push(this.items[key]); } return values; }; Dictionary.prototype.size = function() { return Object.keys(this.items).length; }; Dictionary.prototype.getItems = function() { return this.items; }; /** * createDictionary * * @static * @method createDictionary * @returns {Dictionary} return Dictionary instance * @example * var dictionary = aid.createDictionary(); // use has, get, set, remove, clear, keys, values, size, getItems methods */ aid.createDictionary = function createDictionary() { return new Dictionary(); }; // BinarySearchTree node var BinarySearchTreeNode = function(data) { this.data = data; this.left = null; this.right = null; }; // BinarySearchTree node var BinarySearchTree = function() { this.root = null; }; BinarySearchTree.prototype._insertNode = function(node, newNode) { if (!aid.isDefined(node) || !aid.isDefined(newNode)) throw new TypeError('[BinarySearchTree.prototype._insertNode] node and newNode parameters must be defined.'); // duplicated value is not allowed in this BinarySearchTree if (node.data === newNode.data) return; if (newNode.data < node.data) { aid.isDefined(node.left) ? this._insertNode(node.left, newNode) : (node.left = newNode); } else { aid.isDefined(node.right) ? this._insertNode(node.right, newNode) : (node.right = newNode); } }; BinarySearchTree.prototype._searchNode = function(node, data) { if (!aid.isDefined(node)) return false; if (!aid.isDefined(data)) throw new TypeError('[BinarySearchTree.prototype._searchNode] data parameters must be defined.'); if (data < node.data) { return this._searchNode(node.left, data); } else if (data > node.data) { return this._searchNode(node.right, data); } else { return true; } }; BinarySearchTree.prototype._findMinNode = function(node) { if (!aid.isDefined(node)) return null; while (node && aid.isDefined(node.left)) { node = node.left; } return node; }; BinarySearchTree.prototype._removeNode = function(node, data) { if (!aid.isDefined(data)) throw new TypeError('[BinarySearchTree.prototype._removeNode] data parameters must be defined.'); if (!aid.isDefined(node)) return null; if (data < node.data) { node.left = this._removeNode(node.left, data); return node; } else if (data > node.data) { node.right = this._removeNode(node.right, data); return node; } else { // case: remove leaf node if (!aid.isDefined(node.left) && !aid.isDefined(node.right)) { node = null; return node; } // case: remove node has one child node if (!aid.isDefined(node.left)) { node = node.right; return node; } else if (!aid.isDefined(node.right)) { node = node.left; return node; } // case: remove node has two children nodes var aux = this._findMinNode(node.right); node.data = aux.data; node.right = this._removeNode(node.right, aux.data); return node; } }; BinarySearchTree.prototype._minNode = function(node) { if (!aid.isDefined(node)) return null; while (node && aid.isDefined(node.left)) node = node.left; return node.data; }; BinarySearchTree.prototype._maxNode = function(node) { if (!aid.isDefined(node)) return null; while (node && aid.isDefined(node.right)) node = node.right; return node.data; }; BinarySearchTree.prototype._inOrderTraverseNode = function(node, callback) { if (!aid.isFunction(callback)) throw new TypeError( '[BinarySearchTree.prototype._inOrderTraverseNode] Type of callback parameter must be Function.' ); // visit node.left, node.data, node.right if (aid.isDefined(node)) { this._inOrderTraverseNode(node.left, callback); callback(node.data); this._inOrderTraverseNode(node.right, callback); } }; BinarySearchTree.prototype._preOrderTraverseNode = function(node, callback) { if (!aid.isFunction(callback)) throw new TypeError( '[BinarySearchTree.prototype._preOrderTraverseNode] Type of callback parameter must be Function.' ); // visit node.data, node.left, node.right if (aid.isDefined(node)) { callback(node.data); this._preOrderTraverseNode(node.left, callback); this._preOrderTraverseNode(node.right, callback); } }; BinarySearchTree.prototype._postOrderTraverseNode = function(node, callback) { if (!aid.isFunction(callback)) throw new TypeError( '[BinarySearchTree.prototype._postOrderTraverseNode] Type of callback parameter must be Function.' ); // visit node.left, node.right, node.data if (aid.isDefined(node)) { this._postOrderTraverseNode(node.left, callback); this._postOrderTraverseNode(node.right, callback); callback(node.data); } }; BinarySearchTree.prototype.getRoot = function binarySearchTree_getRoot() { return this.root; }; BinarySearchTree.prototype.insert = function binarySearchTree_insert(data) { if (!aid.isDefined(data)) throw new TypeError('[BinarySearchTree.prototype.insert] data parameter must be defined.'); var newNode = new BinarySearchTreeNode(data); if (aid.isDefined(this.root)) { this._insertNode(this.root, newNode); } else { this.root = newNode; } }; BinarySearchTree.prototype.search = function binarySearchTree_search(data) { if (!aid.isDefined(data)) throw new TypeError('[BinarySearchTree.prototype.search] data parameter must be defined.'); return this._searchNode(this.root, data); }; BinarySearchTree.prototype.remove = function binarySearchTree_remove(data) { if (!aid.isDefined(data)) throw new TypeError('[BinarySearchTree.prototype.remove] data parameter must be defined.'); this.root = this._removeNode(this.root, data); }; BinarySearchTree.prototype.min = function binarySearchTree_min() { return this._minNode(this.root); }; BinarySearchTree.prototype.max = function binarySearchTree_max() { return this._maxNode(this.root); }; BinarySearchTree.prototype.inOrderTraverse = function binarySearchTree_inOrderTraverse(callback) { if (!aid.isFunction(callback)) throw new TypeError('[BinarySearchTree.prototype.inOrderTraverse] Type of callback parameter must be Function.'); this._inOrderTraverseNode(this.root, callback); }; BinarySearchTree.prototype.preOrderTraverse = function binarySearchTree_preOrderTraverse(callback) { if (!aid.isFunction(callback)) throw new TypeError('[BinarySearchTree.prototype.preOrderTraverse] Type of callback parameter must be Function.'); this._preOrderTraverseNode(this.root, callback); }; BinarySearchTree.prototype.postOrderTraverse = function binarySearchTree_postOrderTraverse(callback) { if (!aid.isFunction(callback)) throw new TypeError( '[BinarySearchTree.prototype.postOrderTraverse] Type of callback parameter must be Function.' ); this._postOrderTraverseNode(this.root, callback); }; /** * createBinarySearchTree * * @static * @method createBinarySearchTree * @returns {BinarySearchTree} return BinarySearchTree instance * @example * var binarySearchTree = aid.createBinarySearchTree(); // use getRoot, insert, search, remove, min, max, inOrderTraverse, preOrderTraverse, postOrderTraverse methods */ aid.createBinarySearchTree = function createBinarySearchTree() { return new BinarySearchTree(); }; // Graph function Graph() { this.vertices = []; this.adjacencyList = aid.createDictionary(); } Graph.prototype.addVertex = function(vertex) { if (array.indexOf(this.vertices, vertex) >= 0) throw new Error('[Graph.prototype.addVertex] this.vertices already has the same vertex.'); this.vertices.push(vertex); this.adjacencyList.set(vertex, []); }; Graph.prototype.addEdge = function(fromVertex, toVertex) { if (array.indexOf(this.vertices, fromVertex) < 0) throw new Error('[Graph.prototype.addEdge] this.vertices has not fromVertex.'); if (array.indexOf(this.vertices, toVertex) < 0) throw new Error('[Graph.prototype.addEdge] this.vertices has not toVertex.'); this.adjacencyList.get(fromVertex).push(toVertex); this.adjacencyList.get(toVertex).push(fromVertex); }; Graph.prototype.bfs = function(fromVertex, callback) { // breadth-first serach if (array.indexOf(this.vertices, fromVertex) < 0) throw new Error('[Graph.prototype.bfs] this.vertices has not fromVertex.'); var neighbors = this.adjacencyList.get(fromVertex); if (!neighbors || neighbors.length <= 0) throw new Error('[Graph.prototype.bfs] fromVertex is not connected to any vertices.'); if (aid.isDefined(callback) && !aid.isFunction(callback)) throw new TypeError('[Graph.prototype.bfs] Type of callback parameter must be undefined or null or Function.'); // colors has 3 types of vertex color // 'white' : vertex is not visited // 'grey' : vertex is visited but hasn't been explored