I am working on an exercise that is based on the reduce exercise from underscore.js. Now, I have a solution that works but I believe it can be done better since I used a lot of copy pasting in my Frankenstein solution.
The Solution I found
This is the solution I have come up with:
_.reduce = function (collection, iteratee, accumulator, context) {
if (Array.isArray(collection)) {
var returnVal = accumulator !== undefined ? accumulator : collection[0];
if (accumulator !== undefined) {
for (var i = 0; i < collection.length; i++) {
returnVal = iteratee.call(
context,
returnVal,
collection[i],
i,
collection
);
}
} else {
for (var i = 1; i < collection.length; i++) {
returnVal = iteratee.call(
context,
returnVal,
collection[i],
i,
collection
);
}
}
console.log(returnVal);
return returnVal;
} else if (typeof collection === "object") {
var keys = Object.keys(collection);
var returnVal =
accumulator !== undefined ? accumulator : collection[keys[0]];
if (accumulator !== undefined) {
for (var i = 0; i < keys.length; i++) {
returnVal = iteratee.call(
context,
returnVal,
collection[keys[i]],
keys[i],
collection
);
}
} else {
for (var i = 1; i < keys.length; i++) {
returnVal = iteratee.call(
context,
returnVal,
collection[keys[i]],
keys[i],
collection
);
}
}
console.log(returnVal);
return returnVal;
}
};
As you can see, I distinguish between a collection that is an array, and a collection that is an object. This is my first conditional. Within this conditional I have another conditional for both the array and the object. This conditional checks whether accumulator
is not undefined. If true
, it runs the for loop
from the first iteration, if false
, it runs the for loop
from the second iteration. I do this because when accumulator is undefined, it has to be the first value in the collection, and if I would then run the for loop
from the first iteration, the first value would appear double.
What I have tried
I have tried adding a conditional inside the for loop
to check if accumulator
is undefined
, if it was undefined
I would continue
. This however did not work because accumulator stays undefined
obviously.
I also tried to add that conditional in the for loop
and start the for loop
from the second iteration, and the conditional would add the first value of the collection to returnVal
manually (if accumulator
is undefined
). This also did not work because it messed up the results and gave it back in the wrong order.
What I am expecting
I don’t know if it is possible, but I would like to shorten my code to make it more readable, and just better code in general. I think it is possible to shorten this code, I just can’t see where and how. If there are any other improvements to be made, I would gladly hear about them to as it will help me become a better coder!!
Thanks in advance.
ps. I cannot use built-in functions like forEach()
, map()
, reduce()
and filter()
.
2
Answers
Here a simplified code. It’s used a single
for
loop by setting the initial indexi
based on whetheraccumulator
isundefined
or not. Ifaccumulator
is undefined, it starts from index1
, skipping the first iteration. Ifaccumulator
is defined, it will start from index0
, including the first iteration. And it’s also better to uselet
instead ofvar
.In the true spirit of functional programming, you can reduce (no pun intended) your code dramatically by building on top of code that already does roughly what you want. What you want is to iterate either an object or an array (or any other type of collection that Underscore might support in the future), and
_.find
and_.each
already do exactly that.As for treating the first iteration specially, a state variable works very well in this case. This is what such an approach might look like:
(I have no idea why that script error at the end appears, but it is probably specific to the snippet runner.)
Coincidentally, I have worked on a branch in Underscore where I did something similar, but more sophisticated. The iteratee changes identity temporarily in order to achieve the same end result, so you could say that the iteratee itself is the state variable. You can read it here: https://github.com/jashkenas/underscore/blob/eaba5b58fa8fd788a5be1cf3b66e81f8293f70f9/modules/_createReduce.js.
Greetings from the current Underscore maintainer, I wish you much luck discovering Underscore and functional programming!