I don't see any flaws in your logic... but...
I know you've basically just ported the referenced JavaScript example, but I'm not really fond of the implementation of initialValue
.
Instead, have a look at UnderscoreCF's reduce method: (which was itself ported from UnderscoreJS)
/**
* @header _.reduce(collection, iterator, memo, [context]) : any
* @hint Also known as inject and foldl, reduce boils down a collection of values into a single value. Memo is the initial state of the reduction, and each successive step of it should be returned by iterator.
* @example sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);<br />=> 6
*/
public any function reduce(obj = this.obj, iterator = _.identity, memo, this = {}) {
var outer = {};
if (structKeyExists(arguments, "memo")) {
outer.initial = memo;
}
_.each(arguments.obj, function(value, index, collection, this) {
if (!structKeyExists(outer, "initial")) {
memo = value;
outer.initial = true;
}
else {
memo = iterator(memo, value, index, this);
}
}, arguments.this);
return memo;
}
Granted it does lean on the _.each method, so it can be a little difficult to wrap your head around. The important bit is:
You can omit the initialValue
value and it will be correct no matter what data type is being reduced. Since you have a default value of an empty string, if you're working with numerics, you pretty much have to set an initial value.
On the other hand, the way UnderscoreCF's implementation works is that, if you've not specified a value for memo (initialValue) then it uses the first item in the array as the initial value, and skips the first iteration.
So let's look at two (somewhat contrived) examples:
//string concat
arrayReduce(['A','d','a','m'], function(memo, item){
return memo & item;
});
This (above) will concatenate the characters together into a string. It would work via either implementation of reduce (yours or UnderscoreCF's).
//sum
arrayReduce([1,2,3,4], function(memo, item){
return memo + item;
});
This one does a sum, similarly to your example, except that your implementation of reduce will (probably? I haven't tested to see what would happen...) choke because the callback is expecting numerics but memo is defaulted to an empty string for the first iteration.
Hopefully this is clear. It's a subtle difference.
In writing this answer I've thought of a potential bug in the Underscore implementation: What if you're concatenating a string but you only wanted to include lower-cased characters?
arrayReduce(['A','d','a','m'], function(memo, item){
var asciiCode = asc(item);
return memo & ( asciiCode >= 97 && asciiCode <= 122 ? item : '');
});
You would expect the result to be: dam
but would probably get back Adam
with UnderscoreCF's implementation. It would be easily mitigated by passing ''
for the initialValue argument to reduce, but it is worth noting.
Food for thought! :)