Initial value of reduce based on condition

I have an array that I’m using reduce to walk though, most of the time I do not want to set the initial value but if the size of the array is 1 then I want to set the initial value to 0. I tried doing something like this.
myArray.reduce((a,b,index) => myCallBack(), (myArray.length === 1) ? 0 : null);

But this doesn’t seem to act as reduce does when the initial value has not been assigned. Is there a way to do what I want or should I rethink my solution?

By default, reduce uses the first element of the array as the initial value. If you set an initial value, then the reduce also iterates over the first value, so I don’t think what you want is do-able?

Side note: null and undefined are different things. Using undefined might work, but overall this seems like a 'Bad Idea`™. Code shouldn’t do surprising things. This would seem to be a surprising thing.

I agree but reduce already does surprising things just by the nature of how it iterates over the array being different if you set an initial value or not. In a way I’m trying to do this to recover from that surprising thing. My code worked for 59 test cases but not when the Array only had one value in which case Reduce just sends back the value in the array without even running the callback once. So I was hoping to avoid having to rearchitect my solution but if undefined doesn’t work then I think I’m going to have to.

Just for the record in case someone is looking at this for their own solution. undefined did not work for what I’m doing. It’s not very elegant but for now, this is what worked.

if (myArray.length === 1) { 
     // my loop callBack here 
   } else { myArray.reduce((a,b, index) => myCallBack() ); }

I wouldn’t call the default behavior surprising. It is well documented. It is surprising and unexpected that your code has some magic behavior on the first iteration to take the first array value and the next value and convert it into what you are trying to create instead of consistently setting an initial value and doing the exact same thing no matter the length of the array.

You are correct in that it’s well documented. I’m saying it’s surprising because the behavior is different based on if an initial value is set or not and if the size of the array changes without being able to set the default initial value behavior based on the array size. But I got it working now.

Whether or not the initial value is used has nothing to do with length of the array being reduced. If an initial value is provided, then that is used. If not, then the first value of the array is taken as the initial value. The length of the array doesn’t change that.

The other way around the size of the array (if it’s only one value) determines if there is a default initial value or not. (if you set one then it’s there if you don’t on an array larger than 1 then the first value in the array is the default initial value if the array is of size 1 and you don’t define the initial value then the initial value returns as undefined.) Because it just returns the value without calling the callback at all. (which will probably teach me not to have dependent side effects in my callback)

This just is not true. The only thing that decides if there is a caller provided initial value or if the first element of the array is used is whether or not the caller provided an initial value. The documentation is clear on this point.

In you need side effects, then you should be using forEach.

I’m sorry to butt in. What exactly is your goal in using reduce here? That method is used for combining all the values of an array down into a single value.

So if you’re summing up an array of numbers, a reasonable initial value would be 0.
For the combined product of an array of numbers, you would initialize to 1.
For building up a string you could start with "".
For building up an object you could start with {}.

If you can’t come up with a reasonable initial value for your use case, that sounds pretty smelly to me. Are you sure reduce is the right tool for this job?

1 Like

Well put! This is what I was circling but should have said outright!