0

I have an array of numbers and some limit.

const a = [1,2,1,3,4,1,2];
const limit = 5;

What's the most javascripty way of reducing this array to the index such that the sum from the beginning to the index would exceed the limit?

I've done

function findIndex(nums, limit) {
      let s = 0;
      for (let [index, num] of nums.entries()) {
        s += num;
        if (s >= limit) {
          return index;
        }
      }
      return nums.length;
    }
findIndex([1,2,1,3,4,1,2], 5)
3

Is there a more neat way of doing this using reduce or something else?

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
Davo
  • 526
  • 3
  • 19

2 Answers2

3

As you're after the index, you could look at using the array method .findIndex(). The below code adds to sum for each item in arr, and then checks whether the new accumulated sum value is greater than or equal to the limit. If it is, the callback for findIndex will return true, giving you the index of the item it returned true for. If the callback function to findIndex doesn't return true for any values, it will result in -1, which you can then check before you return to see if you need to return the array length or the found index:

const findIndex = (arr, limit) => {
  let sum = 0;
  const idx = arr.findIndex((num, idx) => (sum+=num) >= limit);
  return idx > 0 ? idx : arr.length; 
}

console.log(findIndex([1,2,1,3,4,1,2], 5));
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
  • 1
    Or perhaps more simply: `(ns, limit, sum = 0) => ns .findIndex ((n) => (sum += n) > limit)`. – Scott Sauyet Nov 29 '21 at 23:07
  • 1
    @ScottSauyet Ah good suggestion, creating the local variable in the arguments of the function is a neat way to keep the arrow function body nice and concise – Nick Parsons Nov 29 '21 at 23:28
  • I like it and do it a lot, but it has its downside. If passed to a function that supplies additional parameters, things can get screwed up. Consider `const sum = (ns, t = 0) => ns .length ? sum (ns .slice (1), t + ns [0]) : t`. Then `sum ([1, 2, 3, 4])` yields `10` as expected, but `[[1, 2, 3], [2, 3, 5]] .map (sum)` yields the incorrect `[6, 11]`, because `map` supplies the index and the whole array as additional parameters, and so for the second array, we start the `sum` at the index of `1` instead of `0`. It's a useful technique, but we do have to be careful with it. – Scott Sauyet Nov 29 '21 at 23:38
  • @ScottSauyet I see, that's a good point and definitely something to look out for. That issue reminds me of [this](https://stackoverflow.com/questions/262427/why-does-parseint-yield-nan-with-arraymap) popular question with `parseInt()` – Nick Parsons Nov 30 '21 at 00:51
2

Since you need the

1) index upto which the sum is greater or equal to limit

2) You want to return early as soon as the sum exceeds limit

then you should go for old school for loop. There is no need to accumulate the index and value using nums.entries()

function findIndex(nums, limit) {
  let sum = 0;
  for (let i = 0; i < nums.length; ++i) {
    sum += nums[i];
    if (sum >= limit) return i;
  }
  // Return something explicitely to 
  // indicate that sum can't exceeds limit.
  // You may return -1 if you like
}
console.log(findIndex([1, 2, 1, 3, 4, 1, 2], 5));
DecPK
  • 24,537
  • 6
  • 26
  • 42