The code below is a reimplementation of the _.reduce() method. It's not mine, but I am using it to get to grips with how _.reduce works. It is currently failing on two tests:
- should be able to reduce a collection to a single value - AssertionError: expected 'aabcdabcdabcdabcd' to equal 'abcd'
- should support initial state AssertionError: expected 'initabcdabcdabcdabcd' to equal 'initabcd'
_.reduce = function (list, iteratee, memo, context) {
if (context) iteratee = iteratee.bind(context);
_.each(list, (elem, index) => {
if (memo === undefined) {
memo = elem;
memo =iteratee(memo, elem, index, list);
} else memo = iteratee(memo, elem, index, list);
});
return memo;
};
I can't understand why this is happening. It looks to me as if this should run as expected. Can anyone provide further information?
UPDATE I was able to solve the 2nd error thanks to @georg spotting the issues with my _.each() function. The first error remains, but is slightly different:
- should be able to reduce a collection to a single value AssertionError: expected 'aabcd' to equal 'abcd'
This is the test code that relates to the error messages
var mocks = {
arr: ['a','b','c','d'], // >= 4 elements, all should be truthy
obj: {a:1,b:2,c:3,d:4}, // >= 4 values, all should be truthy
halfTruthyArr: [null,'b',null,'d'], // >= 4 elements, half should be falsy
halfTruthyObj: {a:1,b:null,c:3,d:null}, // >= 4 values, half should be falsy
string: 'This is a string.',
reverseString: function (string) {
if (typeof string === 'string') return string.split('').reverse().join('');
}
};
describe('reduce', function () {
afterEach(function () {
called = false;
});
it('should be able to reduce a collection to a single value', function () {
_.reduce(mocks.arr, function (accumulator, el, i, arr) {
arr[i].should.equal(el);
return accumulator.toString() + el.toString();
}).should.equal(mocks.stringifiedArrElms);
});
georg already pointed this out in his answer, but I think I can make it a bit more explicit: you are using the first element twice. The following snippet of your code is meant to use the first element of the list when no initial accumulator is provided:
We can substitute
elem
formemo
in the assignments to see how the first element ends up being used twice:The solution is to simply not invoke
iteratee
on the first element, as georg already hinted:georg also suggested using a unique
Symbol()
to more reliably indicate the first element, rather thanundefined
which might also be a return value of youriteratee
halfway through the collection. An alternative approach would be to simply use a boolean state variable (below namedfirst
):