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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
// 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// 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:
1 2 |
// Basic "group by - count" var out = mapReduce(data, _map, _reduce, "Country"); |
This yields the following result:
1 2 3 4 |
[{"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