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

后端 未结 4 1673
陌清茗
陌清茗 2021-01-29 01:34
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

相关标签:
4条回答
  • 2021-01-29 01:54

    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));

    0 讨论(0)
  • 2021-01-29 01:55

    You need parenthesis to execute the function ()

    sum(arr, 0)
    

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

    0 讨论(0)
  • 2021-01-29 02:01

    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/lodash@4.17.15/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/lodash@4.17.15/lodash.min.js"></script>

    0 讨论(0)
  • 2021-01-29 02:06

    call it as follows:

    console.log(sum(arr, 0)());
    
    0 讨论(0)
提交回复
热议问题