Skip to content

Commit

Permalink
[UPDATE] tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Planeshifter committed Jun 23, 2015
1 parent 527a94c commit 2012b6f
Show file tree
Hide file tree
Showing 7 changed files with 1,199 additions and 178 deletions.
143 changes: 76 additions & 67 deletions lib/index.js
Expand Up @@ -2,99 +2,108 @@

// MODULES //

var isArray = require( 'validate.io-array' ),
isObject = require( 'validate.io-object' ),
isFunction = require( 'validate.io-function' ),
isBoolean = require( 'validate.io-boolean-primitive' ),
isNumber = require( 'validate.io-number-primitive' );
var isNumber = require( 'validate.io-number-primitive' ),
isnan = require( 'validate.io-nan' ),
isArray = require( 'validate.io-array' ),
isArrayLike = require( 'validate.io-array-like' ),
isMatrixLike = require( 'validate.io-matrix-like' ),
ctors = require( 'compute-array-constructors' ),
matrix = require( 'dstructs-matrix' ),
validate = require( './validate.js' );


// FUNCTIONS //

var subtract1 = require( './array.js' ),
subtract2 = require( './accessor.js' ),
subtract3 = require( './deepset.js' ),
subtract4 = require( './matrix.js' );


// SUBTRACT //

/**
* FUNCTION: subtract( arr, x[, opts] )
* FUNCTION: subtract( x, y[, opts] )
* Computes an element-wise subtraction.
*
* @param {Number[]|Array} arr - input array
* @param {Number[]|Array|Number} x - either an array of equal length or a scalar
* @param {Number|Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} x - input value
* @param {Number|Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} y - either an array or matrix of equal dimension or a scalar
* @param {Object} [opts] - function options
* @param {Boolean} [opts.copy=true] - boolean indicating whether to return a new array
* @param {Boolean} [opts.copy=true] - boolean indicating if the function should return a new data structure
* @param {Function} [opts.accessor] - accessor function for accessing array values
* @returns {Number[]} output array
* @param {String} [opts.path] - deep get/set key path
* @param {String} [opts.sep="."] - deep get/set key path separator
* @param {String} [opts.dtype="float64"] - output data type
* @returns {Number|Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} value(s) after subtraction
*/
function subtract( arr, x, opts ) {
var isArr = isArray( x ),
copy = true,
arity,
clbk,
function subtract( x, y, options ) {
/* jshint newcap:false */
var opts = {},
ctor,
err,
out,
len,
i;

if ( !isArray( arr ) ) {
throw new TypeError( 'subtract()::invalid input argument. Must provide an array. Value: `' + arr + '`.' );
}
if ( !isArr && !isNumber( x ) ) {
throw new TypeError( 'subtract()::invalid input argument. Second argument must either be an array or number primitive. Value: `' + x + '`.' );
dt,
d;
if ( isNumber( x ) || isnan( x ) ) {
if ( !isNumber( y ) ) {
throw new TypeError( 'subtract()::invalid input argument. Second argument must a number primitive when x is a number. Value: `' + x + '`.' );
}
return x - y;
}
if ( arguments.length > 2 ) {
if ( !isObject( opts ) ) {
throw new TypeError( 'subtract()::invalid input argument. Options argument must be an object. Value: `' + opts + '`.' );
err = validate( opts, options );
if ( err ) {
throw err;
}
if ( opts.hasOwnProperty( 'copy' ) ) {
copy = opts.copy;
if ( !isBoolean( copy ) ) {
throw new TypeError( 'subtract()::invalid option. Copy option must be a boolean primitive. Option: `' + copy + '`.' );
}
}
if ( isMatrixLike( x ) ) {
if ( !isMatrixLike( y ) && !isNumber( y ) ) {
throw new TypeError( 'subtract()::invalid input argument. Second argument must be matrix-like or a number primitive. Value: `' + x + '`.' );
}
if ( opts.hasOwnProperty( 'accessor' ) ) {
clbk = opts.accessor;
if ( !isFunction( clbk ) ) {
throw new TypeError( 'subtract()::invalid option. Accessor must be a function. Option: `' + clbk + '`.' );
if ( opts.copy !== false ) {
dt = opts.dtype || 'float64';
ctor = ctors( dt );
if ( ctor === null ) {
throw new Error( 'subtract()::invalid option. Data type option does not have a corresponding array constructor. Option: `' + dt + '`.' );
}
arity = clbk.length;
// Create an output matrix:
d = new ctor( x.length );
out = matrix( d, x.shape, dt );
} else {
out = x;
}
return subtract4( out, x, y );
}
len = arr.length;
if ( copy ) {
out = new Array( len );
} else {
out = arr;
}
// Case 1: x is an array
if ( isArr ) {
if ( len !== x.length ) {
throw new Error( 'subtract()::invalid input argument. Array to be added must have a length equal to that of the input array.' );
if ( isArrayLike( x ) ) {
if ( !isArrayLike( y ) && !isNumber( y ) ) {
throw new TypeError( 'subtract()::invalid input argument. Second argument must be array-like or a number primitive. Value: `' + x + '`.' );
}
if ( arity === 3 ) { // clbk implied
for ( i = 0; i < len; i++ ) {
out[ i ] = clbk( arr[i], i, 0 ) - clbk( x[i], i, 1 );
}
// Handle deepset first...
if ( opts.path ) {
opts.sep = opts.sep || '.';
return subtract3( x, y, opts.path, opts.sep );
}
else if ( clbk ) {
for ( i = 0; i < len; i++ ) {
out[ i ] = clbk( arr[i], i ) - x[ i ];
}
// Handle regular, typed, and accessor arrays next...
if ( opts.copy === false ) {
out = x;
}
else {
for ( i = 0; i < len; i++ ) {
out[ i ] = arr[ i ] - x[ i ];
else if ( opts.dtype || !isArray( x ) ) {
dt = opts.dtype || 'float64';
ctor = ctors( dt );
if ( ctor === null ) {
throw new TypeError( 'subtract()::invalid input argument. Unrecognized/unsupported array-like object. Provide either a plain or typed array. Value: `' + x + '`.' );
}
out = new ctor( x.length );
}
}
// Case 2: accessor and scalar
else if ( clbk ) {
for ( i = 0; i < len; i++ ) {
out[ i ] = clbk( arr[i], i ) - x;
else {
out = new Array( x.length );
}
}
// Case 3: scalar
else {
for ( i = 0; i < len; i++ ) {
out[ i ] = arr[ i ] - x;
if ( opts.accessor ) {
return subtract2( out, x, y, opts.accessor );
}
return subtract1( out, x, y );
}
return out;
return NaN;
} // end FUNCTION subtract()


Expand Down
190 changes: 190 additions & 0 deletions test/test.accessor.js
@@ -0,0 +1,190 @@
/* global describe, it, require */
'use strict';

// MODULES //

var // Expectation library:
chai = require( 'chai' ),

// Module to be tested:
subtract = require( './../lib/accessor.js' );


// VARIABLES //

var expect = chai.expect,
assert = chai.assert;


// TESTS //

describe( 'accessor subtract', function tests() {

it( 'should export a function', function test() {
expect( subtract ).to.be.a( 'function' );
});

it( 'should perform an element-wise subtraction of a scalar using an accessor', function test() {
var data, actual, expected;

data = [
{'x':0},
{'x':1},
{'x':2},
{'x':3}
];
actual = new Array( data.length );
actual = subtract( actual, data, 1, getValue );

expected = [
-1,
0,
1,
2
];

assert.deepEqual( actual, expected );

function getValue( d ) {
return d.x;
}

});

it( 'should perform an element-wise subtraction of an array using an accessor', function test() {
var data, actual, expected, y;

data = [
{'x':0},
{'x':1},
{'x':2},
{'x':3}
];

y = [
0,
1,
2,
3
];

actual = new Array( data.length );
actual = subtract( actual, data, y, getValue );

expected = [
0,
0,
0,
0
];

assert.deepEqual( actual, expected );

function getValue( d, i ) {
return d.x;
}

});

it( 'should perform an element-wise subtraction of another object array using an accessor', function test() {
var data, actual, expected, y;

data = [
{'x':0},
{'x':1},
{'x':2},
{'x':3}
];

y = [
{'y':0},
{'y':1},
{'y':2},
{'y':3}
];

actual = new Array( data.length );
actual = subtract( actual, data, y, getValue );

expected = [
0,
0,
0,
0
];

assert.deepEqual( actual, expected );

function getValue( d, i, j ) {
if ( j === 0 ) {
return d.x;
} else {
return d.y;
}
}

});

it( 'should return empty array if provided an empty array', function test() {
assert.deepEqual( subtract( [], [], 1, getValue ), [] );
function getValue( d ) {
return d.x;
}
});

it( 'should handle non-numeric values by setting the element to NaN', function test() {
var data, actual, expected, y;

data = [
{'x':1},
{'x':null},
{'x':3}
];
actual = new Array( data.length );
actual = subtract( actual, data, 1, getValue );

expected = [ 0, NaN, 2 ];

assert.deepEqual( actual, expected );

// numeric array
y = [ 1, 2, 3 ];
actual = new Array( data.length );
actual = subtract( actual, data, y, getValue );
expected = [ 0, NaN, 2 ];

function getValue( d, i ) {
return d.x;
}

// object array
y = [
{'y':1},
{'y':2},
{'y':3}
];
actual = new Array( data.length );
actual = subtract( actual, data, y, getValue2 );
expected = [ 0, NaN, 2 ];

function getValue2( d, i, j ) {
if ( j === 0 ) {
return d.x;
} else {
return d.y;
}
}

});

it( 'should throw an error if provided an array to be subtracted which is not of equal length to the input array', function test() {
expect( foo ).to.throw( Error );
function foo() {
subtract( [], [1,2], [1,2,3], getValue );
}
function getValue( d ) {
return d;
}
});

});

0 comments on commit 2012b6f

Please sign in to comment.