almostjs-core
ALMOsT is an AgiLe MOdel Transformation framework for JavaScript
This repository contains a low level graph traversing and aggregation framework used by ALMOsT. It is mainly manted as a generic graph to tree transformation framework.
Installation
$ npm install almost-core
The Pipeline
The transformation follows the following steps
- Graph Traversing during this phase the input graph (JSON Object) is traversed and a set of analysis points are set
- Analysis Execution the analysis points are execute one at a time and results are collected
- Results Aggregation the results are aggregated to generated a final tree
This is an high level view over the system, under the hood we optimize the process in order to interleave analyis points execution and aggregation. This allows us to reduce the final memory footprint.
The Transformer
A transformer is a reusable function which hides the previously presented pipeline.
It can be constructed using the createTransformer(traverse, [reduce])
maker function.
var core = require('almost-core');
var transform = core.createTransformer(traverse);
var output1 = transform(input1);
var output2 = transform(input2);
// ...
var outputN = transform(inputN);
The arguments of createTransformer are the following:
- traverse
function (input, emit)
it is responsible of traversing the graph and "emit" a set of functions which represent the analysis points on the graph. Each function emitted will be invoked once (potentially out of order), with the input object as parameter - reduce [optional] 'function (accumulator, value)', it is responsible of aggregate the results of the analysis points (by default the results are concatenated)
Basic Examples
toPairs
This example creates a transformer which transforms an Object into an Array of [key, value]
pairs.
var core = require('almost-core');
var toPairs = core.createTransformer(function (input, emit) {
Object.keys(input).forEach(function (key) {
emit(function (object) { return object[key]; });
});
});
toPairs({a: 1, b: 2, c: 3}) // result: [['a', 1], ['b', 2], ['c', 3]]
fromPairs
This example creates a transformer which transforms an a Array of [key, value]
pairs into an Object.
var core = require('almost-core');
var fromPairs = core.createTransformer(function (input, emit) {
input.forEach(function (pair) {
emit(function () { var object = {}; object[pair[0]] = pair[1]; return object; });
});
}, function (accumulator, value) {
Object.keys(value).forEach(function (key) {
accumulator[key] = value[key];
});
return accumulator;
});
fromPairs([['a', 1], ['b', 2], ['c', 3]]) // result: {a: 1, b: 2, c: 3}
Reducer
In order to make custom reduction policies, we provide a reduce(iteratee, [accumulator], [terminate])
maker function.
var core = require('almost-core');
var first = core.reduce(function (accumulator) { return accumulator; });
var sum = core.reduce(function (accumulator, value) { return accumulator + value; }, 0);
var avg = core.reduce(
function (accumulator, value) {
accumulator.sum += value;
accumulator.count += 1;
return accumulator;
},
{sum: 0, count: 0},
function (accumulated) {
if (accumulated.count > 0) {
return accumulated.sum / accumulated.count;
}
}
);
We provide a set of helpers to generate complex reduction policies:
none([error])
if even one value is generated an exception is thrown (useful in conjunction withmerge
)single([error])
if more than one value is generated an exception is thrown (useful in conjunction withmerge
)first([default])
it returns the first value encountered (if thedefault
argument is passed it will be considered as first element if none are generated)last([default])
it returns the last value encountered (if thedefault
argument is passed it will be considered as first element in the sequence)concat()
it concatenates all the encountered values in an arrayflatten()
it concatenates all the encountered values in an array (arrays are flattened in single elements)flattenDeep()
it concatenates all the encountered values in an array (arrays are flattened in single elements recursively)merge([policy], [specials])
all the encountered objects will be merged using the last value encountered for each property (if thepolicy
argument is provided it will be used to reduce the different values encountered for each property, if thespecials
argument is provided it is expected to be an object with the form{key: policy, ...}
the policies defined will be used instead of the default one for the related key)mergeOrSingle()
if objects are encountered it recursively merges them with the same policy, if arrays are encountered it concatenates and recursively merges them with the same policy, if anything else is encountered it behaves likesingle
groupBy(key, [policy])
it returns an object which keys are the unique values of thekey
filed in each input and the value is the reduction of all the inputs with the same key, by defaultconcat
lazy(policy)
if at least one input is presentpolicy
policy is applied normally, if no input is present it is like the policy was never there
Example
fromPairs 2.0
This example creates a transformer which transforms an a Array of [key, value]
pairs into an Object.
var core = require('almost-core');
var fromPairs = core.createTransformer(function (input, emit) {
input.forEach(function (pair) {
emit(function () { var object = {}; object[pair[0]] = pair[1]; return object; });
});
}, core.merge());
fromPairs([['a', 1], ['b', 2], ['c', 3]]) // result: {a: 1, b: 2, c: 3}
ALMOsT Model Merging
This example creates a transformer which transforms an a Array of {elements:[...], relations: [...], metadata: {}}
intermediate models into an a final Object.
var core = require('almost-core');
var M2MReduce = core.merge(
core.none('Just elements, realtions and metadata should be generated'),
{
elements: core.flatten(),
relations: core.flatten(),
metadata: core.merge()
}
);