Skip to content

Instantly share code, notes, and snippets.

@HyphnKnight
Last active October 11, 2016 16:56
Show Gist options
  • Select an option

  • Save HyphnKnight/0f1a55d5b6980288cb82a1e09d0f20bc to your computer and use it in GitHub Desktop.

Select an option

Save HyphnKnight/0f1a55d5b6980288cb82a1e09d0f20bc to your computer and use it in GitHub Desktop.
/* 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