Skip to main content
Code Review

Return to Question

replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

This code was written as a solution to this question: http://stackoverflow.com/questions/41398108/balance-array-of-numbers-to-equal-1#41398108 https://stackoverflow.com/questions/41398108/balance-array-of-numbers-to-equal-1#41398108 where numerous comments bring up potential pitfalls which I think are accommodated as best as possible here.

This code was written as a solution to this question: http://stackoverflow.com/questions/41398108/balance-array-of-numbers-to-equal-1#41398108 where numerous comments bring up potential pitfalls which I think are accommodated as best as possible here.

This code was written as a solution to this question: https://stackoverflow.com/questions/41398108/balance-array-of-numbers-to-equal-1#41398108 where numerous comments bring up potential pitfalls which I think are accommodated as best as possible here.

Tweeted twitter.com/StackCodeReview/status/816223219321569280
added 8 characters in body
Source Link
Heslacher
  • 50.9k
  • 5
  • 83
  • 177

I am looking for feedback on:

What the code does:

I am looking feedback on:

What code does:

I am looking for feedback on:

What the code does:

Source Link

Balance array of numbers to maintain distribution but sum up to a goal

I am looking feedback on:

  • any inputs on unforeseen use-cases where the code would be incorrect. (floating-point errors, etc).

  • my method of using a "static class" acceptable

  • is using the default of 10 decimal places a good default or should I not round/truncate to decimal places if possible

Acceptable suggestions include a refactor of logic if it makes it more readable/correct.

Code must be runnable with latest chrome/FF and IE9+. That means no ES6 suggestions.

What code does:

Given an array, this "static" class will balance/equally distribute the values to maintain the original distribution as close as possible while also summing up to a certain value (goal).

It has the benefit of using asking for the return array/answer to be a certain amount of decimal places.

This code was written as a solution to this question: http://stackoverflow.com/questions/41398108/balance-array-of-numbers-to-equal-1#41398108 where numerous comments bring up potential pitfalls which I think are accommodated as best as possible here.

Here is example outcomes:

var a = [0.33333333331, 0.33333333331, 0.33333333333];
console.log(Balancer.balance(a, 1, 3)); //0.333, 0.333, 0.334
var a = [1, 55, 22, 67];
console.log(Balancer.balance(a, 69699, 0)); //480, 26438, 10575, 32206
var a = [10, 10, 10, 10, 15000];
console.log(Balancer.balance(a, 17000, 0)); //11, 11, 11, 11, 16956
var a = [ 0.00005 ,5 ,0.00005 ,0.00005 ,0.00005 ,0.00005 ,0.00005 ,0.00005 ]
console.log(Balancer.balance(a, -5, 5)); // [-0.00005, -4.99965, -0.00005, -0.00005, -0.00005, -0.00005, -0.00005, -0.00005]
var a = [
 0.0015974718789698097
 ,0.755383581038094
 ,0.13950473043946954
 ,0.0011978091842754731
 ,0.005126875727346068
 ,0.0042250281407886295
 ,0.0001720958819913952
 ,0.0047584144830165875
 ,0.0835073272489086
 ,0.00016098907002300275
 ,0.0028037075787230655
 ,0.0014378579473690483
 ,0.00012411138102484662
 ]
console.log(Balancer.balance(a, 1, 3)); // [0.002, 0.755, 0.14, 0.001, 0.005, 0.004, 0, 0.005, 0.084, 0, 0.003, 0.001, 0]

Executable code: http://jsbin.com/motedexifo/1/edit?js,console

var Balancer = (function() {
 return {
 isNumber: function(input) {
 return !isNaN(parseInt(input, 10)) && !isNaN(Number(input));
 },
 divideEach: function(array, divisor) {
 for (var i = 0; i < array.length; i++) {
 array[i] /= divisor;
 }
 return array;
 },
 
 indexOfMin: function(arr) {
 if (arr.length === 0) {
 return -1;
 }
 var min = arr[0];
 var minIndex = 0;
 for (var i = 1; i < arr.length; i++) {
 if (arr[i] < min) {
 minIndex = i;
 min = arr[i];
 }
 }
 return minIndex;
 },
 indexOfMax: function(arr) {
 if (arr.length === 0) {
 return -1;
 }
 var max = arr[0];
 var maxIndex = 0;
 for (var i = 1; i < arr.length; i++) {
 if (arr[i] > max) {
 maxIndex = i;
 max = arr[i];
 }
 }
 return maxIndex;
 },
 convertToFactors: function(array) {
 var total = this.sum(array);
 return this.divideEach(array, total);
 },
 sum: function(array) {
 var total = 0;
 for (var i = 0; i < array.length; i++) {
 if (this.isNumber(array[i])) {
 total += Number(array[i]);
 }
 }
 return total;
 },
 round: function(number, places) {
 var multiplier = Math.pow(10, places);
 return Math.round(number * multiplier) / multiplier;
 },
 roundEach: function(array, places) {
 for (var i = 0; i < array.length; i++) {
 if (this.isNumber(array[i])) {
 array[i] = this.round(array[i], places);
 }
 }
 return array;
 },
 balance: function(array, goal, places) {
 places = (places !==undefined) ? places : 10;
 
 var copy = JSON.parse(JSON.stringify(array)); // deep copy
 var factors = this.convertToFactors(array);
 this.roundEach(copy, places);
 goal = this.round(goal, places);
 var sum = this.round(this.sum(copy), places);
 var old_difference = null;
 while (goal !== sum) {
 var difference = goal - sum;
 if (old_difference == difference) {
 var idx = (difference > 0) ? this.indexOfMax(factors) : this.indexOfMin(factors);
 copy[idx] += this.round(difference, places);
 } else {
 for (i = 0; i < factors.length; i++) {
 copy[i] += this.round(difference * factors[i], places);
 }
 }
 sum = this.round(this.sum(copy), places);
 old_difference = difference;
 }
 
 this.roundEach(copy, places);
 return copy
 },
 }
})();
default

AltStyle によって変換されたページ (->オリジナル) /