Last active
October 11, 2016 16:56
-
-
Save HyphnKnight/0f1a55d5b6980288cb82a1e09d0f20bc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* functional.js */ | |
| /* Type Testing */ | |
| function isNull( x ) { | |
| return x === null; | |
| } | |
| function isUndefined( x ) { | |
| return typeof x === 'undefined'; | |
| } | |
| function isNullOrUndefined( x ) { | |
| return isNull( x ) || isUndefined( x ); | |
| } | |
| function isString( x ) { | |
| return typeof x === 'string'; | |
| } | |
| function isNumber( x ) { | |
| return typeof x === 'number'; | |
| } | |
| function isBoolean( x ) { | |
| return typeof x === 'boolean'; | |
| } | |
| function isDate( date ) { | |
| return Object.prototype.toString.call( date ) === '[object Date]'; | |
| } | |
| function isArray( x ) { | |
| return Array.isArray( x ); | |
| } | |
| function isFunction( x ) { | |
| return ( {} ).toString.call( x ) === '[object Function]'; | |
| } | |
| function isComparable( value ) { | |
| return typeof value === 'number' || | |
| typeof value === 'boolean' || | |
| typeof value === 'string' || | |
| typeof value === 'symbol' || | |
| typeof value === 'function'; | |
| } | |
| function isFalsey( value ) { | |
| return isNullOrUndefined( value ) || | |
| ( isBoolean( value ) && !value ) || | |
| ( isString( value ) && value === '' ) || | |
| ( isNumber( value ) && isNaN( value ) ); | |
| } | |
| function isEqual( valueA, valueB ) { | |
| if ( typeof valueA !== typeof valueB ) { | |
| return false; | |
| } else if ( typeof valueA === 'undefined' ) { | |
| return true; | |
| } else if ( valueA === null || valueB === null || isComparable( valueA ) ) { | |
| return valueA === valueB; | |
| } else if ( isDate( valueA ) && isDate( valueB ) ) { | |
| return valueA.getTime() === valueB.getTime(); | |
| } else if ( Array.isArray( valueA ) && Array.isArray( valueB ) ) { | |
| return valueA.length === valueB.length && !valueA.find( ( val, index ) => !isEqual( valueB[ index ], val ) ); | |
| } else if ( valueA instanceof Map && valueB instanceof Map ) { | |
| return valueA.size === valueB.size && isEqual( Array.from( valueA.entries() ), Array.from( valueB.entries() ) ); | |
| } else if ( valueA instanceof Set && valueB instanceof Set ) { | |
| return valueA.size === valueB.size && isEqual( Array.from( valueA ), Array.from( valueB ) ); | |
| } else if ( !!valueA.__store__ && !!valueB.__store__ ) { | |
| return isEqual( valueA.__store__, valueB.__store__ ); | |
| } else if ( Object.keys( valueA ).length === Object.keys( valueA ).length ) { | |
| return !Object.keys( valueA ).find( val => !isEqual( valueA[ val ], valueB[ val ] ) ); | |
| } else { | |
| return false; | |
| } | |
| } | |
| /* Functions */ | |
| function identity( x ) { | |
| return x; | |
| } | |
| function curry( func, ...args ) { | |
| return function curryInside( ...insideArgs ) { | |
| return func( ...args.concat( insideArgs ) ); | |
| } | |
| } | |
| function compose( ...functions ) { | |
| return function composeInside( ...args ) { | |
| const firstFunc = functions.shift(); | |
| return functions.reduce( function composeReduce( result, func ) { | |
| return func( result ); | |
| }, firstFunc.apply( null, args ) ); | |
| } | |
| } | |
| function promiseCompose( ...functions ) { | |
| return function promiseComposeInside( ...args ) { | |
| return functions.reduce( function composeReduce( result, func ) { | |
| return result.then( func ) | |
| }, Promise.resolve( args ) ) | |
| .catch( function promiseComposeError( error ) { | |
| throw error; | |
| } ); | |
| } | |
| } | |
| function memoize( func, keyFunc = JSON.stringify ) { | |
| const memoizeDB = {}; | |
| return function memoizeInside( ...args ) { | |
| const key = keyFunc( ...args ); | |
| if ( typeof memoizeDB[ key ] !== undefined ) { | |
| return memoizeDB[ key ]; | |
| } else { | |
| memoizeDB[ key ] = func( ...args ); | |
| return memoizeDB[ key ]; | |
| } | |
| } | |
| } | |
| /* Array Functions */ | |
| function times( length, func = identity ) { | |
| return ( new Array( length ).fill( 0 ) ).map( ( _, i ) => func( i, length ) ); | |
| } | |
| function difference( array, targetArray ) { | |
| return array.filter( val => targetArray.indexOf( val ) === -1 ); | |
| } | |
| function flatten( array ) { | |
| return [].concat.apply( [], array ); | |
| } | |
| function unique( array ) { | |
| return array.filter( ( value, index, self ) => self.indexOf( value ) === index ); | |
| } | |
| function uniqueBy( array, func = identity ) { | |
| return unique( array.map( value => func( value ) ) ); | |
| } | |
| function groupBy( array, func = identity ) { | |
| return array.reduce( ( groupedResults, value ) => { | |
| const key = func( value ); | |
| if ( isArray( groupedResults[ key ] ) ) { | |
| groupedResults[ key ].push( value ); | |
| } else { | |
| groupedResults[ key ] = [ value ]; | |
| } | |
| return groupedResults; | |
| }, {} ); | |
| } | |
| function invoke( array ) { | |
| return array.map( func => { | |
| func(); | |
| return func; | |
| } ); | |
| } | |
| function filter( array, func = identity ) { | |
| return array.filter( func ); | |
| } | |
| function map( array, func = identity ) { | |
| return array.map( func ); | |
| } | |
| function concat( ...arrays ) { | |
| return flatten( arrays ); | |
| } | |
| function sort( array, func = identity, ascending = true ) { | |
| let sortFunc; | |
| if ( !ascending ) { | |
| sortFunc = function descendingSort( a, b ) { | |
| return func( a ) - func( b ); | |
| }; | |
| } else { | |
| sortFunc = function ascendingSort( a, b ) { | |
| return func( b ) - func( a ); | |
| }; | |
| } | |
| return Array.from( array ).sort( sortFunc ); | |
| } | |
| function reverse( array ) { | |
| return Array.from( array ).reverse() | |
| } | |
| function heuristicFind( array, func = identity ) { | |
| return sort( array, func, false )[ 0 ]; | |
| } | |
| /* Map Functions */ | |
| function mapFilter( map, func = identity ) { | |
| const newMap = Map( {} ); | |
| map.forEach( function mapFilterInside( value, key ) { | |
| if ( func( value ) ) { | |
| newMap.set( key, value ); | |
| } | |
| } ); | |
| return newMap; | |
| } | |
| function mapMap( map, func = identity ) { | |
| const newMap = Map( {} ); | |
| map.forEach( function mapMapInside( value, key ) { | |
| newMap.set( key, func( value ) ); | |
| } ); | |
| return newMap; | |
| } | |
| function mapDifferenceKey( map, targetMap ) { | |
| return mapFilter( map, function mapDifferenceKeyInside( value, key ) { | |
| return !targetMap.get( key ); | |
| } ) | |
| } | |
| function mapDifferenceValue( map, targetMap ) { | |
| return mapFilter( map, function mapDifferenceValueInside( value ) { | |
| return !targetMap.has( value ); | |
| } ) | |
| } | |
| function mapMerge( map, targetMap ) { | |
| const newMap = Map( {} ); | |
| targetMap.forEach( function mapTargetMergeInside( value, key ) { | |
| newMap.set( key, value ); | |
| } ); | |
| map.forEach( function mapMergeInside( value, key ) { | |
| newMap.set( key, value ); | |
| } ); | |
| return newMap; | |
| } | |
| function mapGroupBy( map, func = identity ) { | |
| const newMap = Map( {} ); | |
| map.forEach( function mapGroupByInside( value, key ) { | |
| const groupKey = func( value ); | |
| if ( !newMap.get( groupKey ) ) { | |
| newMap.set( groupKey, new Map( { [ key ]: value } ) ); | |
| } else { | |
| newMap.get( groupKey ).set( key, value ); | |
| } | |
| } ) | |
| return newMap; | |
| } | |
| /* Objects */ | |
| function objectGet( obj, path ) { | |
| return path.split( '.' ).reduce( function getReduce( value, key ) { | |
| return isUndefined( value ) ? undefined : value[ key ]; | |
| }, obj ); | |
| } | |
| function objectForEach( obj, func = identity ) { | |
| Object.keys( obj ).forEach( key => func( obj[ key ] ) ); | |
| return obj; | |
| } | |
| function objectMap( obj, func = identity ) { | |
| const newObj = {}; | |
| Object.keys( obj ).forEach( key => { | |
| newObj[ key ] = func( obj[ key ] ) | |
| } ); | |
| return obj; | |
| } | |
| function objectFind( obj, func = identity ) { | |
| const foundKey = Object.keys( obj ).find( key => func( obj[ key ], key ) ); | |
| return !!foundKey ? [ obj[ foundKey ], foundKey ] : foundKey; | |
| } | |
| function objectToArray( obj ) { | |
| const newArray = []; | |
| Object.keys( obj ).forEach( key => newArray.push( obj[ key ] ) ); | |
| return newArray; | |
| } | |
| /* Math */ | |
| function clamp( num, lower, upper ) { | |
| return num > upper ? upper : ( num < lower ? lower : num ); | |
| } | |
| function random( max = 1, min = 0 ) { | |
| return Math.random() * ( max - min ) + min; | |
| } | |
| function lerp( a, b, dt ) { | |
| return a + ( a - b ) * dt; | |
| } | |
| function uniqueId() { | |
| return Math.floor( ( new Date() ).getTime() * 1000 + Math.random() * 1000 ); | |
| } | |
| /* Chain */ | |
| class Chain { | |
| constructor( array ) { | |
| this.value = array; | |
| } | |
| forEach( func ) { | |
| this.value.forEach( func ); | |
| return this; | |
| } | |
| map( func ) { | |
| this.value = map( this.value, func ); | |
| return this; | |
| } | |
| filter( func ) { | |
| this.value = filter( this.value, func ); | |
| return this; | |
| } | |
| difference( array ) { | |
| this.value = difference( this.value, array ); | |
| return this; | |
| } | |
| flatten() { | |
| this.value = flatten( this.value ); | |
| return this; | |
| } | |
| unique() { | |
| this.value = unique( this.value ); | |
| return this; | |
| } | |
| uniqueBy( func ) { | |
| this.value = uniqueBy( this.value, func ); | |
| return this; | |
| } | |
| groupBy( func ) { | |
| return groupBy( this.value, func ); | |
| } | |
| invoke() { | |
| this.value = invoke( array ); | |
| return this; | |
| } | |
| concat( array ) { | |
| this.value = concat( this.value, array ); | |
| return this; | |
| } | |
| sort( func, ascending = true ) { | |
| this.value = sort( this.value, func, ascending ); | |
| return this; | |
| } | |
| reverse() { | |
| this.value = reverse( this.value ); | |
| return this; | |
| } | |
| } | |
| class ChainMap { | |
| constructor( map ) { | |
| this.value = map; | |
| } | |
| forEach( func ) { | |
| this.value.forEach( func ); | |
| return this; | |
| } | |
| map( func ) { | |
| this.value = mapMap( this.value, func ); | |
| return this; | |
| } | |
| filter( func ) { | |
| this.value = mapFilter( this.value, func ); | |
| return this; | |
| } | |
| differenceKey( targetMap ) { | |
| this.value = mapDifferenceKey( this.value, targetMap ); | |
| return this; | |
| } | |
| differenceValue( targetMap ) { | |
| this.value = mapDifferenceValue( this.value, targetMap ); | |
| return this; | |
| } | |
| groupBy( func ) { | |
| this.value = mapGroupBy( this.value, func ); | |
| return this; | |
| } | |
| merge( targetMap ) { | |
| this.value = mapMerge( this.value, targetMap ); | |
| return this; | |
| } | |
| } | |
| export { | |
| // Types | |
| isNull, | |
| isUndefined, | |
| isNullOrUndefined, | |
| isString, | |
| isNumber, | |
| isBoolean, | |
| isArray, | |
| isFunction, | |
| isDate, | |
| isFalsey, | |
| isEqual, | |
| // Functions | |
| identity, | |
| curry, | |
| compose, | |
| promiseCompose, | |
| memoize, | |
| // Arrays | |
| times, | |
| difference, | |
| unique, | |
| uniqueBy, | |
| flatten, | |
| groupBy, | |
| invoke, | |
| sort, | |
| heuristicFind, | |
| chain, | |
| // Maps | |
| mapMap, | |
| mapFilter, | |
| mapDifferenceKey, | |
| mapDifferenceValue, | |
| mapGroupBy, | |
| mapMerge, | |
| chainMap, | |
| // Objects | |
| objectGet, | |
| objectForEach, | |
| objectMap, | |
| objectFind, | |
| objectToArray, | |
| // Math | |
| clamp, | |
| random, | |
| uniqueId, | |
| lerp | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment