JavaScript Concurrency: lightweight map reduce
by Adam Boduch
JavaScript
'use strict';
// This generator will keep keep iterating, as
// long as "next()" is called. It's expecting
// a value as well, so that it can call the
// "iteratee()" function on it, and yield the
// result.
function* genMapNext(iteratee) {
var input = yield null;
while (true) {
input = yield iteratee(input);
}
}
// Our array of values we want to map.
var array = [ 'a', 'b', 'c', 'b', 'a' ];
// A "mapper" generator. We pass an iteratee
// function as an argument to "genMapNext()".
var mapper = genMapNext(x => x.toUpperCase());
// Our starting point for the reduction.
var reduced = {};
// We have to call "next()" to bootstrap the
// generator.
mapper.next();
// Now we can start iterating over the array.
// The "mapped" value is yielded from the
// generator. The value we want mapped is fed
// into the generator by passing it to "next()".
for (let item of array) {
let mapped = mapper.next(item).value;
// Our reduction logic takes the mapped value,
// and adds it to the "reduced" object, counting
// the number of duplicate keys.
if (reduced.hasOwnProperty(mapped)) {
reduced[mapped]++;
} else {
reduced[mapped] = 1;
}
}
console.log('reduced', reduced);
// → reduced { A: 2, B: 2, C: 1 }
// This generator is a more useful mapper than
// "genMapNext()" because it doesn't rely on values
// coming into the generator through "next()".
//
// Instead, this generator accepts an iterable, and
// an iteratee function. The iterable is then
// iterated-over, and the result of the iteratee
// is yielded.
function* genMap(iterable, iteratee) {
for (let item of iterable) {
yield iteratee(item);
}
}
// Creates our "mapped" generator, using an iterable
// data source, and an iteratee function.
var mapped = genMap(array, x => x.toUpperCase());
var reduced = {}
// Now we can simply iterate over our genrator, instead
// of calling "next()". The job of each loop iteration
// is to perform the reduction logic, instead of having
// to call "next()".
for (let...