ds-algo-study
Version:
Just experimenting with publishing a package
228 lines (227 loc) • 6.02 kB
JavaScript
/*
! Clone an object
clone(x)
Can clone any primitive type, array, and object.
If x has a function clone, this function will be invoked to clone the object.
>param {} x
>return {} clone
*/
export function clone( x ) {
const type = typeof x;
// immutable primitive types
if (
type === "number" ||
type === "string" ||
type === "boolean" ||
x === null ||
x === undefined
) {
return x;
}
// use clone function of the object when available
if ( typeof x.clone === "function" ) {
return x.clone();
}
// array
if ( Array.isArray( x ) ) {
return x.map( function ( value ) {
return clone( value );
} );
}
if ( x instanceof Date ) return new Date( x.valueOf() );
// object
return mapObject( x, clone );
}
/*
! Apply map to all properties of an object
>param {Object} object
>param {function} callback
>return {Object} Returns a copy of the object with mapped properties
*/
export function mapObject( object, callback ) {
const clone = {};
for ( const key in object ) {
if ( hasOwnProperty( object, key ) ) {
clone[ key ] = callback( object[ key ] );
}
}
return clone;
}
/*
! Extend object a with the properties of object b
>param {Object} a
>param {Object} b
>return {Object} a
*/
export function extend( a, b ) {
for ( const prop in b ) {
if ( hasOwnProperty( b, prop ) ) {
a[ prop ] = b[ prop ];
}
}
return a;
}
/*
! Deep test equality of all fields in two pairs of arrays or objects.
Compares values and functions strictly (ie. 2 is not the same as '2').
>param {Array | Object} a
>param {Array | Object} b
>returns {boolean}
*/
export function deepStrictEqual( a, b ) {
let prop, i, len;
if ( Array.isArray( a ) ) {
if ( !Array.isArray( b ) ) {
return false;
}
if ( a.length !== b.length ) {
return false;
}
for ( i = 0, len = a.length; i < len; i++ ) {
if ( !deepStrictEqual( a[ i ], b[ i ] ) ) {
return false;
}
}
return true;
} else if ( typeof a === "function" ) {
return a === b;
} else if ( a instanceof Object ) {
if ( Array.isArray( b ) || !( b instanceof Object ) ) {
return false;
}
for ( prop in a ) {
// noinspection JSUnfilteredForInLoop
if ( !( prop in b ) || !deepStrictEqual( a[ prop ], b[ prop ] ) ) {
return false;
}
}
for ( prop in b ) {
// noinspection JSUnfilteredForInLoop
if ( !( prop in a ) || !deepStrictEqual( a[ prop ], b[ prop ] ) ) {
return false;
}
}
return true;
} else {
return a === b;
}
}
/*
! Recursively flatten a nested object.
>param {Object} nestedObject
>return {Object} Returns the flattened object
*/
export function deepFlatten( nestedObject ) {
const flattenedObject = {};
_deepFlatten( nestedObject, flattenedObject );
return flattenedObject;
}
// helper function used by deepFlatten
function _deepFlatten( nestedObject, flattenedObject ) {
for ( const prop in nestedObject ) {
if ( hasOwnProperty( nestedObject, prop ) ) {
const value = nestedObject[ prop ];
if ( typeof value === "object" && value !== null ) {
_deepFlatten( value, flattenedObject );
} else {
flattenedObject[ prop ] = value;
}
}
}
}
/*
! Test whether the current JavaScript engine supports Object.defineProperty
>returns {boolean} returns true if supported
*/
export function canDefineProperty() {
// test needed for broken IE8 implementation
try {
if ( Object.defineProperty ) {
Object.defineProperty( {}, "x", {
get: function () {}
} );
return true;
}
} catch ( e ) {}
return false;
}
/*
! Attach a lazy loading property to a constant.
The given function `fn` is called once when the property is first requested.
>param {Object} object Object where to add the property
>param {string} prop Property name
>param {Function} valueResolver Function returning the property value. Called
without arguments.
*/
export function lazy( object, prop, valueResolver ) {
let _uninitialized = true;
let _value;
Object.defineProperty( object, prop, {
get: function () {
if ( _uninitialized ) {
_value = valueResolver();
_uninitialized = false;
}
return _value;
},
set: function ( value ) {
_value = value;
_uninitialized = false;
},
configurable: true,
enumerable: true,
} );
}
/*
! Get a nested property from an object
>param {Object} object
>param {string | string[]} path
>returns {Object}
*/
export function get( object, path ) {
if ( typeof path === "string" ) {
if ( isPath( path ) ) {
return get( object, path.split( "." ) );
} else {
return object[ path ];
}
}
let child = object;
for ( let i = 0; i < path.length; i++ ) {
const key = path[ i ];
child = child ? child[ key ] : undefined;
}
return child;
}
/*
! Set a nested property in an object
Mutates the object itself
If the path doesn't exist, it will be created
>param {Object} object
>param {string | string[]} path
>param {} value
>returns {Object}
*/
export function set( object, path, value ) {
if ( typeof path === "string" ) {
if ( isPath( path ) ) {
return set( object, path.split( "." ), value );
} else {
object[ path ] = value;
return object;
}
}
let child = object;
for ( let i = 0; i < path.length - 1; i++ ) {
const key = path[ i ];
if ( child[ key ] === undefined ) {
child[ key ] = {};
}
child = child[ key ];
}
if ( path.length > 0 ) {
const lastKey = path[ path.length - 1 ];
child[ lastKey ] = value;
}
return object;
}