23Jan/12Off
Browser MapReduce + Charting
Having dabbled in Mongo a bit, I was curious if there was an analog for MapReduce style functionality in browser JavaScript.
I found a small script as a starting point, but made some minor modifications to support array inputs:
// mapper should return an array of [{key:'somekey', value:'somevalue'}]
// reducer should return a single {key:'somekey', value:'somevalue'}
function mapReduce(i, mapper, reducer, property) {
var intermediate = [];
var output = [];
if(i && i.constructor == Array)
{
for(var x = 0; x < i.length; x++)
{
var value = i[x][property];
var key = property;
intermediate = intermediate.concat(mapper(key, value));
}
}
else
{
for (var key in i)
{
var value = i[key];
intermediate = intermediate.concat(mapper(key, value));
}
}
var groups = groupBy(intermediate);
for (var key in groups)
{
var values = groups[key];
output.push(reducer(key, values));
}
return output;
}
// list should be [{key:k, value:v}, ....] where key may be repeated.
// returns [{key, [v1, v2, v3...]}, ...] where key is *not* repeated.
function groupBy(list) {
var ret = {};
for (var i = 0; i < list.length; i++) {
var key = list[i].key;
var value = list[i].value;
if (!ret[key]) {
ret[key] = [];
}
ret[key].push(value);
}
return ret;
}
I then plugged in my custom map/reduce functions:
// Random data set -- could come from anywhere.
var data = [
{Country: "US", Type:"B", ProductCode: "001"},
{Country: "US", Type:"B", ProductCode: "001.A"},
{Country: "US", Type:"Z", ProductCode: "001.B"},
{Country: "UK", Type:"A", ProductCode: "002"},
{Country: "US", Type:"Z", ProductCode: "003"},
{Country: "FR", Type:"B", ProductCode: "003.A"},
{Country: "DE", Type:"B", ProductCode: "003.C"},
{Country: "DE", Type:"T", ProductCode: "004"},
{Country: "UK", Type:"R", ProductCode: "004.R"},
{Country: "UK", Type:"B", ProductCode: "005"}
];
// Custom mapper
function _map(key, value)
{
var result = [];
result.push({key:value, value:1});
return result;
}
// Custom reducer
function _reduce(key, values)
{
var sum = 0;
for(var i = 0; i < values.length; i++)
{
sum += values[i];
}
return {key: key, value: sum};
}
Now can I call it like so:
// Basic "group by - count" var out = mapReduce(data, _map, _reduce, "Country");
This yields the following result:
[{"key":"US","value":4},
{"key":"UK","value":3},
{"key":"FR","value":1},
{"key":"DE","value":2}]
Pretty nifty! And this data can then be fed into flot for charting purposes.
Sample (with flot example) is attached: MapReduce.zip
