Home > other >  Increase or decrease numbers of an array so the sum of them always remains 100
Increase or decrease numbers of an array so the sum of them always remains 100

Time:03-19

Let's say there is an array of x numbers, and the sum of them are 100.

Each number increases or decreases linearly with a step. Each time a number increases the rest have to decrease evenly so that the sum of the numbers doesn't exceed 100. Likewise each time a number decreases the rest have to increase evenly so that the sum of the numbers doesn't go below 100. If a value is causing the sum to exceed or go below 100 the action has to be disallowed with a message to the user.

Let's say that array A: [20,20,20,20,20]

A[2]  = 4
//the array A has to become somehow automatically: [19,19,24,19,19]

There is 3 problems on this. Firstly if a number exceeds a value, that will make the rest go below 0, and I don't want that. Example:

A: [-5,100,-5,-5,-5]

And the other one is related with the step. I don't know how much it should increase or decrease (maybe based on the length of the array).

Right now I have step = 1 / A.length

So that if a number increases with step the rest of them have to decrease with step / A.length - 1 (minus one cause I don't count the number the user changed)

And vice-versa

Basically I am trying to do a percentage increment or decrement based on user value (down or up). Can you propose me the logic I have to follow, or some JavaScript code?

EDIT:

I have already implemented something in angular.

At first the values are all equal and sum is 100:

enter image description here

If i increase the first number with the button ( > ), the rest will decrease, and so on..

enter image description here

I am not posting code because its buggy and very meshed up, I just want the logic or a paradigm in code so I can implement it in my app. The check-boxes in the numbers are meant to lock that value so it doesn't increment or decrement.

CodePudding user response:

You could check if there is a value for decrementing and add all changes to the sum for incrementing a value.

const
    change = (array, index) => {
        const decrement = 1 / array.length;
        let sum = 0;
        for (let i = 0; i < array.length; i  ) {
            if (i === index || array[i] - decrement < 0) continue;
            array[i] -= decrement;
            sum  = decrement;
        }
        array[index]  = sum;
        return array;
    },
    values = [20, 20, 20, 20, 20];
    
console.log(...change(values, 1));
console.log(...change(values, 1));
console.log(...change(values, 2));
console.log(...change(values, 1));
console.log(...change(values, 4));
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

Details commented example below

// Utility function
const log = data => console.log(JSON.stringify(data));

const arr1 = [20, 20, 20, 20, 20];
const arr2 = [-5, 115, -5, -5];

/**@function
 *@name incDec
 *@description Increase/decrease a number by the given index of a given 
 * array by a given number and inversely decrease/increase the rest of 
 * the numbers evenly until the sum of all numbers within the array is 
 * the same as it was originally.
 *@param {number} index - Index number of the number being changed.
 *@param {number} by - Number to decrease/increase first parameter by.
 *@param {array<number>} array - The array of numbers
 *@returns {array<number>} A new array of modified numbers
 */
const incDec = (index, by, array) => {
  /* 
  Chrome doesn't divide negative numbers correctly.
  Simple divison in of itself needs to be simplified.
  So a ❉ will denote this as the reason.
  */
  let mod = Math.abs(by);
  /*
  Return a new array with the targeted number modified by >by<.
  */
  const newArr = array.map((num, idx) => idx == index ? num   by : num);

  const div = array.length - 1; //❉
  /*
  Divide the number it changed by, by the number of remaining numbers 
  in the array.
  */
  let quo = mod / div;
  quo = by < 0 ? -Math.abs(quo) : quo; //❉
  // Return a new array that has the modified numbers.
  return newArr.map((num, idx) => idx == index ? num : num - quo);
};

log(incDec(3, 8, arr1));
log(incDec(2, -8, arr2));
.as-console-row::after {
  width: 0;
  font-size: 0;
}

.as-console-row-code {
  width: 100%;
  word-break: break-word;
}

  • Related