<CharlieDigital/> Programming, Politics, and uhh…pineapples

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

Posted by Charles Chen

Filed under: Dev Comments Off
Comments (0) Trackbacks (0)

Sorry, the comment form is closed at this time.

Trackbacks are disabled.