_F
Functional chaining in js.
Usage
bower install Hypercubed/_F
- Include the
_F.js
script into your app. By default should be atbower_components/_F/_F.js
Testing
Install npm and bower dependencies:
npm installbower installnpm test
Summary of API
Hypercubed/_F is simply global shortcut for composable "d3 style" data accessors functions. For example:
Accessors
_F | Pure JS equivalent |
---|---|
_F() |
function(d) { return d; } |
_F('prop') |
function(d) { return d.prop; } |
_F('prop.prop') |
function(d) { return d.prop.prop; } |
_F('prop.prop.prop') |
function(d) { return d.prop.prop.prop; } |
_F(number) |
function(d) { return d[number]; } |
_F('$index') |
function(d, i) { return i; } |
_F('$this') |
function() { return this; } |
Example
var data = firstname: 'John' lastname: 'Smith' age: 51 /* ... */ ;var _firstname = ; data; // Returns a list of first names
Operators
_F | Pure JS equivalent |
---|---|
_F('prop').eq(value) |
function(d) { return d.prop == value; } |
_F('prop').neq(value) |
function(d) { return d.prop !== value; } |
_F('prop').gt(value) |
function(d) { return d.prop > value; } |
_F('prop').lt(value) |
function(d) { return d.prop < value; } |
_F('prop').gte(value) |
function(d) { return d.prop >= value; } |
_F('prop').lte(value) |
function(d) { return d.prop <= value; } |
_F('prop').in(array) |
function(d) { return array.indexOf(d) > -1; } |
_F('prop').has(value) |
function(d) { return d.prop.indexOf(value) > -1; } |
Example
var _johns = _firstname; data; // returns a list of John's
Chaining
_F | Pure JS equivalent |
---|---|
_F('prop').gt(value).and(fn) |
function(d) { return (d.prop > value) && fn(d); } |
_F('prop').gt(value).or(fn) |
function(d) { return (d.prop > value) || fn(d); } |
_F('prop').gt(value).not(fn) |
function(d) { return (d.prop > value) && !fn(d); } |
_F('prop').gt(value).and().lt(valueB) |
function(d) { return (d.prop > value) && (d.prop < valueB); } |
_F('prop').lt(value).or().gt(valueB) |
function(d) { return (d.prop < value) || (d.prop > valueB); } |
_F('prop').gt(value).not().eq(valueB) |
function(d) { return (d.prop > value) && !(d.prop == valueB); } |
Example
var _age = ;var _twenties = _age; data; // returns a list of John's in their twenties
Sorting
_F | Pure JS equivalent |
---|---|
_F('prop').order(fn) |
function(a,b) { return fn(a.prop,b.prop); } |
_F('prop').order().asc |
function(a,b) { return fn(ascending); } |
_F('prop').order().desc |
function(a,b) { return fn(decending); } |
Example
data; // returns a list of John's in their twenties sorted by age in ascending order
Why?
In JavaScript, especially when using d3, we often write accessor functions like this:
{ return dvalue; }
This simple function returns the value of the value
key when an object is pass to it. For example in the map
function:
values = data;
This is lightweight, simple, and readable. There is nothing wrong with it. Sometimes, however, in order to avoid repeating ourselves so we crete a reusable accessor function like this:
var { return dvalue; };values = data;
Now imagine the object also has a year
key whose values are date objects. We may want to filter like this:
var { return dvalue; };var { return dyear >= '1980 Jan 1'; };values = data;
However, this has a couple of slight drawbacks. First of all you will need to create a new filter every time the date changes; also the Date
constructor is called for every element in the data
array. A better approach is an accessor factory:
var { return { return dyear >= date; };} var _filter = ;values = data;
It's a little ugly but here the Date
constructor is only called once and the _year_filter function returns the accessor. An new accessor can be created any time by calling _year_filter
Now what if we want to filter between two dates. We can do modify our accessor factory:
var { return { return dyear >= dateA && dyear < dateB; };}
but let's say that you have multidimensional data where dateA
and dataB
change independently. You might be tempted to do something like this:
var { return { return dyear >= dateA; };} var { return { return dyear < dateB; };} _year_filter1 = ;_year_filter2 = ; values = data ;
Ok, no we are getting ridiculous. The date constructor is not that expensive. But you can imagine a situation where the values for filters could be very expensive. For example based on aggregated statistics or reading from the DOM.
Ok, at this point let me introduce _F
. _F
is simply a shortcut for all this. For example:
var _value = ;values = data;
The value returned from _F()
in this case is simply the accessor function function(d) { return d.value; }
.
Interesting. How about this:
var _value = ;var _year_filter = ;values = data;
_F('year').gte(somevalue)
is essentially a shortcut for function(d) { return d.year >= somevalue; }
.
It gets better:
var _value = ; var _year_filter = ; values = data;
or how about this:
var _value = ;var _value_filter = _value; var _year = ;var _year_filter = _year ; var _filter = _value_filter; values = data;
Pretty neat?
Acknowledgments
License
Copyright (c) 2014+ Jayson Harshbarger MIT