0.3.1 • Published 5 years ago

@hyperplanes/rutils v0.3.1

Weekly downloads
-
License
MIT
Repository
github
Last release
5 years ago

rutils

A library of javascript functions that implement set and sequence-based operations in a style similar to R. The library provides analogs of seq, union,unique, intersect, setdiff, and identical. It also includes a cartesian method for which there is no analog in base R.

Getting Started

This library is written in modern javascript modules, using webpack and babel to transpile to a more browser-compatible code bundle. If you are not developing with javascript modules using the ES6 import syntax, it is recommended you use the transpiled bundle instead of the original source code.

Running transpiled code in a webpage

The transpiled release code is in dist\rutils.bundle.js. To include this in a web app, copy the bundle to your webserver and include it in a script tag:

<script src="rutils.bundle.js"></script>

Then you can access the methods through the exported rutils object:

var unique=rutils.unique;
var x=[1,1,2,2,3,3];
var unique_x=unique(x); //[1,2,3];

Importing the original methods in a javascript module

The original source files are in src and all of the library methods are exported by src/index.js. You can copy these files to your project and import them in your javascript module:

import {unique} from 'src/index.js';

let x=[1,1,2,2,3,3];
let unique_x=unique(x); //[1,2,3];

Usage

seq - generate sequences of dates or numbers

seq takes an object as it's argument, with the optional properties from,to,length_out,by, and along_with. The last one, along_with is just used to set length_out equal to the length of an existing array. At most one of from,to,by, and length_out or along_with can be left empty and seq will impute it from the other parameters. If a single integer N is passed to seq instead of an object, it will generate an array of integers starting at 1 and counting up by 1 to N.

Generating a sequence of even numbers starting at 2 and ending at 10:

import {seq} from 'src/index.js';

let x=seq({from:2,to:10,by:2}); //[2,4,6,8,10]

Counting the number of quarters since Q1 2010:

import {seq} from 'src/index.js';

let x=seq({from:new Date(2010,0,1), to:new Date(),by:"3 months"}).length;

Counting backwards:

import {seq} from 'src/index.js';

let x=seq({from:10,to:2,by:-2}); //[10,8,6,4,2]

What was the date 100 days ago?

import {seq} from 'src/index.js';

let x=seq({to:new Date(),by:"days", length_out:100})[0];

cumsum - cumulative sum over an array

Cumsum takes an array of numbers and returns an array of the cumulative sum

import {seq} from 'src/index.js';
import {cumsum} from 'src/index.js';

let x=cumsum(seq(3)); //[1,3,6]

cartesian - cartesian product of two or more arrays

cartesian can take any number of arrays as arguments, and will return an array with the cartesian product of the arguments

import {cartesian} from 'src/index.js';

let x=[1,2]
let y=['a','b'];
let result=cartesian(x,y);
// result:
// [
// 	[1,'a'],
// 	[1,'b'],
// 	[2,'a'],
// 	[2,'b']
// ];

cartesian can handle both primitive types like numbers and strings as well as objects like dates and object literals as elements. Note, however, that it will flatten arrays of arrays, which might not be the behavior you're expecting:

let A=[[1,2],[3,4]];
let B=[
	['a','b'],
	['c','d']
	];
let result=cartesian(A,B);
// result:
// [
// 		[1,2,'a','b'],
// 		[1,2,'c','d'],
// 		[3,4,'a','b'],
// 		[3,4,'c','d']
// ]

identical - deep equivalence of two objects

identical(x,y) returns true if the object x is deep-equal to the object y, and false otherwise. Primitive types (string, number, null, undefined, bigint, symbol, boolean) are compared by strict equality (===), dates are compared by valueOf, while all other objects are compared by comparing keys and values, using recursion for nested objects.

let x={
	prop1:1,
	prop2:{
		nested1:'foo',
		nested2:'bar',
		nestedArray:[1,2,3]
	}
}
let y={
	prop1:1,
	prop2:{
		nested1:'foo',
		nested2:'bar',
		nestedArray:[1,2,3]
	}
}
let result=identical(x,y); //true
let result2=identical(NaN,NaN); //also true

One exception to the above rule is that NaN is considered identical to itself, which differs from native javascript where NaN is the only value that is not considered equal to itself. This is because rutils intends to treat NaN as the javascript analog to R's NA.

intersect - set of objects contained in both of two arrays

intersect(A,B) returns the elements of an array A that are identical to an element in array B, as determined by the itentical method above. Note that the returned objects are the exact objects that were in A and not clones of them.

let x={
	foo:'bar'
};
let A=[1,2,x];
let B=[3,2,x,5];
let result=intersect(A,B);//returns [2,x];
let isSameObject=result[1]===x; //returns true, both references point to the same object

setdiff - difference between two arrays

setdiff(A,B) returns the distinct elements of array A that are not identical to any element in array B, as determined by the identical method above. Works the same as intersect in that returned objects are literally elements of A and not clones of them.

let A=[1,2,3];
let B=[3,2,4];
let result=setdiff(A,B); //returns [2,3]

union - set of objects in either of two arrays

union(A,B) returns a new array containing all of the unique elements of A followed by all the unique elements of B. If any two elements are identical, as determined by the identical method above, only the first instance will be returned in the resulting array.

let x={
	foo:'bar'
};
let A=[1,2,x];
let B=[3,2,x,5];
let result=union(A,B);//returns [1,2,x,3,5]

unique - get the first instance of each object in an array

unique(A) returns the first occurance of each object in array A, using the identical method above to compare objects.

let A=[1,2,3,2,4];
let result=unique(A); //returns [1,2,3,4]

max - get the maximum value in an array

max(A) returns the maximum value across its arguments. max differs from native javascript Math.max in that it accepts both arrays and objects as arguments and if the maximal value is a date, it will return the original date object and not an integer value.

let result1=max([1,2,3],[6,4],5); //returns 6
let result2=max([1,2,3]); //returns 3
let result3=max(1,2,3); //returns 3

let a=new Date(2019,1,1);
let b=new Date(2019,0,1);
let c=new Date(2019,2,1);
let result4=max(a,b,c);
console.log(result4); //prints Fri Mar 01 2019 00:00:00 GMT-0500 (Eastern Standard Time)
let result5=c===result4; //returns true because max returns the original object

min - get the minimum value in an array

min(A) returns the minimum value across its arguments. Works the same as max above.

diff - get lagged differences of an array

diff(A,{lag:n,differences:k}) returns an array of the kth differences in array A lagged by n places. The lag and differences parameters are optional, with default value of 1. They must be positive integers. diff also works on date objects but unlike R, will consistently return a result in miliseconds.

let A=[1,2,3,2,4];
let result1=diff(A); //returns [1,1,-1,2]
let result2=diff(A,{differences:2}); //returns [0,-2,3]
let result3=diff(A,{lag:2}); //returns [2,0,1]
let result4=diff(A,{lag:2,differences:2}); //returns [-1]

let B=[new Date(2019,1,1),new Date(2019,0,1),new Date(2019,2,1)];
let result5=diff(B);// returns [-2678400000, 5097600000]

Running the tests

This assumes you have cloned the rutils directory and have Node.js installed. In the command prompt run npm install in th rutils directory to install the required node modules. It may help to also run npm install -g webpack webpack-cli to globally install the command-line tools for webpack.

To build the distribution bundle, run the command

npm run build

To run the unit tests, run

npm start

to launch webpack's dev server. Then open a web browser to http://localhost:8080/test/ (or whatever URL domain webpack tells you). The tests will only run in a browser, such as Chrome, that accepts modular script tags like this:

<script type="module" src="intersectTests.js"></script>

Note, although this uses QUnit for the unit tests, the command qunit won't run the tests because Node.js is not yet compatible with the official ES6 module syntax. Also note that Chrome will refuse to load modular scripts if you open the html file directly in the browser rather than using the dev server.

Every exported function has its own file in the src\js directory, and each file has a corresponding test/[name]Tests.js file in the test directory, with the exception of the sequence related source files which are all tested together in tests/seqTests.js:

"standardize_date_units.js"
"calculate_date_by.js"
"DateMethods.js"
"seq.js"
"seq_Date.js"
"seq_date_primitive.js"
"seq_num.js"
"seq_num_primitive.js"

The unit tests aim for 100 percent code coverage. The file tests/index.html contains script tags for all of the unit test files and therefore will run them all when opened in the browser. Additional html files in test will run specific subsets of the unit tests.

License

This project is licensed under the MIT License - see the LICENSE.md file for details

0.3.1

5 years ago

0.3.0

5 years ago

0.2.1

6 years ago

0.2.2

6 years ago

0.2.0

6 years ago

0.1.1

6 years ago

0.1.0

6 years ago