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:
If i increase the first number with the button ( > ), the rest will decrease, and so on..
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;
}