Mangler.js javascript aggregators

Mangler.js‘ new .aggregate() function helps you do calculations on sets of values with the least amount of coding.

Syntax

As usual, it comes in two flavours: as a static utility function or as a Mangler object method:

Mangler.aggregate(collection, aggregator, options);
Mangler(array).aggregate(aggregator, options);

Parameters:

  • collection: Any array or iterable object to get the values from.
  • aggregator: String identifier of the built-in aggregator function to use. You can also pass your own aggregator function.
  • options: An object with optional parameters. If a string is passed, it is handled as the value option.

Options:

  • value: Rather than using the values in the collection directly, use Mangler.getpath() to get its sub-item.
  • group: Path of the field to use as a group identifier to process the data in batches. Can also be a custom function that returns the group name.

Simple examples

var numbers = [6, 10, 20];
var object = { one: 1, two: 2, three: 3 };

// Calculate the sum of an array of numbers
Mangler(numbers).aggregate('+');  // returns 36

// Calculate the sum of object property values
Mangler.aggregate(object, '+');  // returns 6

var products = [
	{ name: 'cherry', type: 'fruit', price: 3.00 },
	{ name: 'orange', type: 'fruit', price: 2.90 },
	{ name: 'grapes', type: 'fruit', price: 1.00 },
	{ name: 'carrot', type: 'vegetable', price: 2.00 },
	{ name: 'celery', type: 'vegetable', price: 3.50 }
];

// Calculate the average product price
Mangler(products).aggregate('avg', 'price');  // returns 2.48

// Collect all product names into an array
Mangler(products).aggregate('[]', 'name');
// returns ['cherry', 'orange', 'grapes', 'carrot', 'celery']

Built-in aggregators

  • "sum": The sum of all values. Aliases: "+", "add"
  • "mul": Multiply all values together. Alias: "*"
  • "avg": The average of all values.
  • "cnt": The number of values found. Alias: "count"
  • "min": The smallest value. Alias: "<"
  • "max": The largest value. Alias: ">"
  • "array": Returns an array of all values. Alias: "[]"

Grouping

// Count products by type
Mangler(products).aggregate('cnt', { group: 'type' });
// returns object: { fruit: 3, vegetable: 2 }

// Get the lowest prices per product type
Mangler(products).aggregate('min', { value: 'price', group: 'type' });
// returns object: { fruit: 1.00, vegetable: 2.00 }

A custom grouping function takes two parameters (key, item) and returns a group name or identifier, which will become property names of the returned object. key is the array index or object property name of the item within the collection. The following example collects product names grouped by their first letter:

var getFirstLetter = function(k, i) { return i.name[0]; };
Mangler(products).aggregate('[]', { value: 'name', group: getFirstLetter });

Returns:

{
	c: ['cherry', 'carrot', 'celery'],
	g: ['grapes'],
	o: ['orange']
}

Custom aggregators

You can pass your own function instead of an aggregator name. The aggregator function gets called for each item in the collection and updates the result. The final result has to be calculated on every call, as there may or may not be further items in the collection. It only needs to generate a single result, grouping of items is handled outside of this function.

It takes three parameters:

  • k is the key of the item in the collection, either an array index or an object property.
  • v is the value to be processed. Either the item itself, or a part of it extracted by the value option.
  • a is the aggregator object, its value is preserved between aggregator calls. The value of a.count is the number of values processed so far, including the current one (starts at 1). The result has to be put into a.result. On the first call, a.result is undefined.

This is the built-in sum aggregator as an example:

function(k, v, a) {
	a.result = a.count === 1 ? v : a.result + v;
},

Advanced aggregators

Special getters can be used to simplify aggregators and queries. This example registers a special getter to the Date type to group items automatically:

var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

Mangler.registerType(Date, {
	get: function(date, key) {
		if(key === 'dayOfWeek') {
			return days[date.getDay()];
		}
	}
});

var data = [
	{ qty: 1, date: new Date('2015-03-01') },
	{ qty: 4, date: new Date('2015-03-02') },
	{ qty: 3, date: new Date('2015-03-03') },
	{ qty: 7, date: new Date('2015-03-04') },
	{ qty: 4, date: new Date('2015-03-04') },
	{ qty: 9, date: new Date('2015-03-05') },
	{ qty: 2, date: new Date('2015-03-06') },
	{ qty: 1, date: new Date('2015-03-07') },
	{ qty: 3, date: new Date('2015-03-08') },
	{ qty: 2, date: new Date('2015-03-10') },
	{ qty: 1, date: new Date('2015-03-11') },
	{ qty: 4, date: new Date('2015-03-12') },
];

// Get total quantites per day of the week
var result = Mangler(data).aggregate('+', { value: 'qty', group: 'date.dayOfWeek' });

console.log(result);

Result:

{
	Sunday: 4,
	Monday: 4,
	Tuesday: 5,
	Wednesday: 12,
	Thursday: 13,
	Friday: 2,
	Saturday: 1
}
Share Button
Tagged with: ,

Leave a Reply