简体   繁体   中英

Sum all numbers in array until a certain value is reached

I'm writing a function and this is the last piece of the puzzle, which I thought would be easy to deal with, but after some unsuccessful tinkering with for and while loops, I tried looking it up online and still couldn't find an answer. I came across some obscure and complex solutions, but I think there should be a more straightforward way to solve this. For example, if I have an array

[1, 2, 3, 5, 7, 9]

And the argument is 10, the function should return 6 (1 + 2 + 3), because the sum of the values should be <= 10 (or whatever number is passed). If the argument is 4, the function should return 3 (1 + 2) and so on.

You can use a for loop:

 const arg = 10 const arr = [1, 2, 3, 5, 7, 9] let res = 0; const calc = (arr, limit) => { for (num of arr) { if (num + res > limit) { break; } res += num; } return res; } console.log(calc(arr, arg))

.reduce() each current value ( cur ) added to the accumulator ( acc ) and checked vs. the limit ( max ). If acc + cur is less than limit then return acc+ cur , otherwise return acc . Added .sort() if the array happens to be out of order, as per Spectric's comment.

 const array = [1,2,3,5,6,8]; const mixed = [0,7,3,8,2,1]; const A = 10; const B = 15; const closest = (arr, max) => { return arr.sort((a, b) => a - b).reduce((acc, cur) => (acc + cur) > max ? acc : (acc + cur)); } console.log(closest(array, A)); console.log(closest(array, B)); console.log(closest(mixed, B));

One interesting approach is to realize that we want to do a fold (something like Array.prototype.reduce ) but one which we can escape early. The answer from zer00ne does this by choosing to check the condition on every iteration, and just continually returning the accumulator each time the condition is not met. That's fine for many use-cases, but it would be nice to make it more explicit.

I know of two ways to do this. One is to have some signal value that we would return -- probably a symbol -- to say, "We're done, just return the accumulator." The downside is that this function now depends on that external signal value. It's not terrible, and often might be the right solution. It's not hard to write, and I'll leave it as an exercise.

The other technique is to require the callback to explicitly choose whether to continue the iteration or stop by supplying it with next and done functions. If we want to continue, we call next with the next accumulator value. If we're finished, we just call done . Here's a version of that:

 const fold = (fn) => (a) => ([x, ...xs]) => x == undefined ? a : fn (a, x, (r) => fold (fn) (r) (xs), () => a) const sumUpTo = (n) => fold ((a, x, next, done) => a + x > n ? done () : next (a + x)) (0) console .log (sumUpTo (10) ([1, 2, 3, 5, 7, 9])) //=> 6 console .log (sumUpTo (4) ([1, 2, 3, 5, 7, 9])) //=> 3 console .log (sumUpTo (10) ([1, 2, 3, 4, 5, 6])) //=> 10

sumUpTo takes our total value and returns a function that takes a list of numbers. It does this by calling fold using a callback function, the initial value ( 0 for a sum), and eventually passing our list of numbers. That callback does the work we care about. Then fold repeatedly calls it until it runs out of values or done is called.

We can break down the one-liner version above to focus specifically on the callback, if it makes it more clear:

const callback = (n) => (a, x, next, done) => 
  a + x > n 
    ? done () 
    : next (a + x)

const sumUpTo = (n) => fold (callback (n)) (0)

It's a very elegant pattern, to my mind.

Quickest way with smallest code footprint:

 const dataset = [1, 2, 3, 5, 7, 9] const getMaxSum = (dataset, max) => { var sum = 0, num; for( num of dataset ) { if( sum + num > max ) break // very important to break once satisfied sum += num } return sum } console.log( getMaxSum(dataset, 10) ) console.log( getMaxSum(dataset, 20) )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM