ElMuchacho
ElMuchacho

Reputation: 300

Javascript - Counting array elements by reduce method until specific value occurs doesn't give a correct output

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total)=>(num==0 ? total : total+num), 0) 
console.log(sum(arr, 0))

Please check how can I make it work. Did some mistake but don't know what exactly. Output is a function instead of a result.

Upvotes: 0

Views: 1139

Answers (4)

VLAZ
VLAZ

Reputation: 29086

This is awkward to do in .reduce because it goes through the entire array. If we do a naive implementation you can see the problem:

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=>(num==x ? total : total+x), 0) 
console.log(sum(arr, 0))

We now make the check correctly - num==x will return true when x is zero (the value of num). However, the result is wrong because this only returns true once but any other iteration it's still true. And here is the same thing with more logging that describes each step of the process:

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=> {
  const boolCheck = num==x;
  const result = boolCheck ? total : total+x;
  console.log(
`total: ${total}
num: ${num}
x: ${x}
boolCheck: ${boolCheck}
result: ${result}`);

    return result;
  }, 0) 
console.log(sum(arr, 0))

So, you need to add some flag that persists between iterations, so it doesn't get lost.

One option is to have an external flag that you change within the reduce callback:

const arr = [5,6,0,7,8];
const sum = (arr,num) => {
  let finished = false;
  return arr.reduce((total, x) => {
    if(x === num)
      finished = true;
      
    return finished ? total : total+x;
  }, 0)
}
console.log(sum(arr, 0))

Alternatively, you can have that flag internal to the reduce callback and pass it around between calls. It works the same way in the end but makes the callback function pure. At the cost of some unorthodox construct:

const arr = [5,6,0,7,8];
const sum = (arr,num) => {
  return arr.reduce(({total, finished}, x) => {
    if(x === num)
      finished = true;

    total = finished ? total : total+x;

    return {total, finished};
  }, {total: 0, finished: false})
  .total
}
console.log(sum(arr, 0))

If you want to use reduce but you're OK with using other methods, then you can use Array#indexOf to find the first instance of a value and Array#slice the array that contains any value up to the target value:

const arr = [5,6,0,7,8];
const sum = (arr,num) => {
  const endIndex = arr.indexOf(num);
  
  return arr.slice(0, endIndex)
    .reduce((total, x)=> total+x, 0)
}
console.log(sum(arr, 0))

Or in as one chained expression:

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr
  .slice(0, arr.indexOf(num))
  .reduce((total, x)=> total+x, 0);

console.log(sum(arr, 0))

Other libraries may have a takeUntil or takeWhile operation which is even closer to what you want - it gets you an array from the beginning up to a given value or condition. You can then reduce the result of that.

Here is an example of this using Lodash#takeWhile

By using chaining here, Lodash will do lazy evaluation, so it will only go through the array once, instead of scanning once to find the end index and going through the array again to sum it.

const arr = [5,6,0,7,8];
const sum = (arr,num) => _(arr)
  .takeWhile(x => x !== num)
  .reduce((total, x)=>total+x, 0)

console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

As a note, if you are using Lodash, then you may as well use _.sum(). I didn't above just to illustrate how a generic takeUntil/takeWhile looks.

const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => _(arr)
  .takeWhile(x => x !== num)
  .sum()

console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Upvotes: 1

Nick
Nick

Reputation: 147206

Since you need to stop summing values part way through the array, this might be most simply implemented using a for loop:

const arr = [5, 6, 0, 7, 8];
const num = 0;

let sum = 0;
for (let i = 0; i < arr.length; i++) {
  if (arr[i] == num) break;
  sum += arr[i];
}
console.log(sum);

If you want to use reduce, you need to keep a flag that says whether you have seen the num value so you can stop adding values from the array:

const arr = [5, 6, 0, 7, 8];

const sum = (arr, num) => {
  let seen = false;
  return arr.reduce((c, v) => {
    if (seen || v == num) {
      seen = true;
      return c;
    }
    return c + v;
  }, 0);
}

console.log(sum(arr, 0));
console.log(sum(arr, 8));

Upvotes: 1

Ilijanovic
Ilijanovic

Reputation: 14904

You need parenthesis to execute the function ()

sum(arr, 0)

Without parenthesis you store a reference to the function in the variable

Upvotes: 0

Yair Nevet
Yair Nevet

Reputation: 13013

call it as follows:

console.log(sum(arr, 0)());

Upvotes: 0

Related Questions