UNPKG

universe

Version:

The fastest way to query and explore multivariate datasets

252 lines (219 loc) 5.89 kB
'use strict' var _ = require('./lodash') var aggregators = { // Collections $sum: $sum, $avg: $avg, $max: $max, $min: $min, // Pickers $count: $count, $first: $first, $last: $last, $get: $get, $nth: $get, // nth is same as using a get $nthLast: $nthLast, $nthPct: $nthPct, $map: $map, } module.exports = { makeValueAccessor: makeValueAccessor, aggregators: aggregators, extractKeyValOrArray: extractKeyValOrArray, parseAggregatorParams: parseAggregatorParams, } // This is used to build aggregation stacks for sub-reductio // aggregations, or plucking values for use in filters from the data function makeValueAccessor(obj) { if (typeof obj === 'string') { if (isStringSyntax(obj)) { obj = convertAggregatorString(obj) } else { // Must be a column key. Return an identity accessor return obj } } // Must be a column index. Return an identity accessor if (typeof obj === 'number') { return obj } // If it's an object, we need to build a custom value accessor function if (_.isObject(obj)) { return make() } function make() { var stack = makeSubAggregationFunction(obj) return function topStack(d) { return stack(d) } } } // A recursive function that walks the aggregation stack and returns // a function. The returned function, when called, will recursively invoke // with the properties from the previous stack in reverse order function makeSubAggregationFunction(obj) { // If its an object, either unwrap all of the properties as an // array of keyValues, or unwrap the first keyValue set as an object obj = _.isObject(obj) ? extractKeyValOrArray(obj) : obj // Detect strings if (_.isString(obj)) { // If begins with a $, then we need to convert it over to a regular query object and analyze it again if (isStringSyntax(obj)) { return makeSubAggregationFunction(convertAggregatorString(obj)) } // If normal string, then just return a an itentity accessor return function identity(d) { return d[obj] } } // If an array, recurse into each item and return as a map if (_.isArray(obj)) { var subStack = _.map(obj, makeSubAggregationFunction) return function getSubStack(d) { return subStack.map(function(s) { return s(d) }) } } // If object, find the aggregation, and recurse into the value if (obj.key) { if (aggregators[obj.key]) { var subAggregationFunction = makeSubAggregationFunction(obj.value) return function getAggregation(d) { return aggregators[obj.key](subAggregationFunction(d)) } } console.error('Could not find aggregration method', obj) } return [] } function extractKeyValOrArray(obj) { var keyVal var values = [] for (var key in obj) { if ({}.hasOwnProperty.call(obj, key)) { keyVal = { key: key, value: obj[key], } var subObj = {} subObj[key] = obj[key] values.push(subObj) } } return values.length > 1 ? values : keyVal } function isStringSyntax(str) { return ['$', '('].indexOf(str.charAt(0)) > -1 } function parseAggregatorParams(keyString) { var params = [] var p1 = keyString.indexOf('(') var p2 = keyString.indexOf(')') var key = p1 > -1 ? keyString.substring(0, p1) : keyString if (!aggregators[key]) { return false } if (p1 > -1 && p2 > -1 && p2 > p1) { params = keyString.substring(p1 + 1, p2).split(',') } return { aggregator: aggregators[key], params: params, } } function convertAggregatorString(keyString) { // var obj = {} // obj is defined but not used // 1. unwrap top parentheses // 2. detect arrays // parentheses var outerParens = /\((.+)\)/g // var innerParens = /\(([^\(\)]+)\)/g // innerParens is defined but not used // comma not in () var hasComma = /(?:\([^\(\)]*\))|(,)/g return JSON.parse('{' + unwrapParensAndCommas(keyString) + '}') function unwrapParensAndCommas(str) { str = str.replace(' ', '') return ( '"' + str.replace(outerParens, function(p, pr) { if (hasComma.test(pr)) { if (pr.charAt(0) === '$') { return ( '":{"' + pr.replace(hasComma, function(p2 /* , pr2 */) { if (p2 === ',') { return ',"' } return unwrapParensAndCommas(p2).trim() }) + '}' ) } return ( ':["' + pr.replace( hasComma, function(/* p2 , pr2 */) { return '","' } ) + '"]' ) } }) ) } } // Collection Aggregators function $sum(children) { return children.reduce(function(a, b) { return a + b }, 0) } function $avg(children) { return ( children.reduce(function(a, b) { return a + b }, 0) / children.length ) } function $max(children) { return Math.max.apply(null, children) } function $min(children) { return Math.min.apply(null, children) } function $count(children) { return children.length } /* function $med(children) { // $med is defined but not used children.sort(function(a, b) { return a - b }) var half = Math.floor(children.length / 2) if (children.length % 2) return children[half] else return (children[half - 1] + children[half]) / 2.0 } */ function $first(children) { return children[0] } function $last(children) { return children[children.length - 1] } function $get(children, n) { return children[n] } function $nthLast(children, n) { return children[children.length - n] } function $nthPct(children, n) { return children[Math.round(children.length * (n / 100))] } function $map(children, n) { return children.map(function(d) { return d[n] }) }