Testing whether you reach an array’s last item when using reduce() in Javascript

Tags: , ,



I am trying to implement the equivalent of .join() using .reduce() to answer a quiz on executeprogram.com. My code passes the tests, but it seems ugly. The specific issue that I have solved in an ugly way is this: The constructed join() function takes an array and a separator as arguments, but the separator should not be inserted after the last item of the array when constructing the resulting joined output. This means I have to test, when iterating over the array with reduce(), whether I am at the last item (so that I can skip using the separator) [oops: see note below]. Using indexOf or lastIndexOf doesn’t work if there are duplicate values in the array. So I created a counter. I don’t like it. I would love suggestions about how to do this better. Here’s the code:

 function join(arr, separator) {
  let counter = 0;
  if (arr.length === 0) { return(''); }
  else { 
    return (
    arr.reduce((accumulator, item) =>
      {
        if (counter === arr.length - 1) {
          return accumulator + item;
        } else {
          counter += 1; 
          return accumulator + item + separator;
        }
      }, 
      '')
    );
  }
} 

The tests and their expected output are below. Again, the function above passes all the tests, but I think there has to be a better way.

> join(['a'], ',')
Expected: 'a'
> join(['a', 'b'], ',')
Expected: 'a,b' 
> join(['a', 'b', 'c'], '')
Expected: 'abc'
> join(['a', 'b', 'c', 'd'], 'x')
Expected: 'axbxcxd'
> join(['a', 'b'], 'COMMA')
Expected: 'aCOMMAb'
> join(['', '', ''], ',')
Expected: ',,'
> join([], ',')
Expected: ''

NOTE: After seeing all the helpful answers, I realized that the real cause for this ugly code was my mistaken assumption that the separator comes “after” an item. Actually, the separator comes between items, and you can make it come between every item if you insert it before every item except the first one. Then you don’t need to test for the last item. When you remove the assumption that you need to test for the last item, everything is much simpler. So basically, I stated the problem wrong in the first place. Thanks to everyone for the answers!

Answer

As others have pointed out you could use the index and array args in the callback.

But there is another approach. You don’t have to know when you’ve encountered the last array element. You already know what the first element is and you can just use that.

const join = (arr, sep=',') => arr.slice(1).reduce((acc, item) => acc + sep + item, arr[0]);

To further refine the function, take advantage of the little used default initial value of the accumulator. If you do not pass an initial value to the reduce() function, first execution of the callback will be given the first and second elements of the array.

const join = (arr, sep=',') => arr.reduce((acc, item) => acc + sep + item);

And to handle empty array without error…

const join = (arr, sep=',') => arr[0] ? arr.reduce((acc, item) => acc + sep + item) : '';


Source: stackoverflow