2

I'm trying to write a simple program in vanilla JavaScript for weightlifting. The user inputs a certain amount of weight and it returns the specific weight plates to put on each side of the barbell.

I then take that number into a function which subtracts 45 from it to account for the barbell weight then divides that number by 2 which is the amount of weight to put on each side of the bar.

const num = document.getElementById("weightAmount").value;

function getWeightAmount (num) {
const newNum = num - 45;
const halfNum = newNum / 2;
return getWeights(halfNum);
}

I have an array with each weight plate:

let plates = [44, 33, 22, 11, 5.5, 2.75];

I'm having trouble correctly looping through the array to get what I want. If I need, say, 60.5 lbs on each side, it should return 44, 11, 5.5. So I need to figure out which numbers in the plate array fit in the number returned from my first function.

I have an empty array called weights which I want to push the numbers from the plates array into that work with the weight which then is returned.

My question is how do I loop through the plates array to figure out which weights are needed?

adiga
  • 34,372
  • 9
  • 61
  • 83
breakthatbass
  • 356
  • 1
  • 3
  • 11
  • Find the weight that only divides once into your target weight, add it to the barbell and subtract it from the target weight. Repeat until target weight is zero. – Robert Harvey Feb 25 '19 at 16:38
  • You loop through an array with `plates.forEach(function(plate){}}` for `for (let plate of plates) {}` – TKoL Feb 25 '19 at 16:38
  • 2
    Maybe relevant: [Knapsack problem](https://en.wikipedia.org/wiki/Knapsack_problem) – FK82 Feb 25 '19 at 16:57
  • Well, as mentioned by @FK82, it is Knapsack, but a slightly modified one. It is actually a modified version of a Change-making problem [https://en.wikipedia.org/wiki/Change-making_problem], which itself is a modified version of Knapsack problem. – poet_ Feb 25 '19 at 17:17
  • @Taylorg: You can either write your own solution by following the answers given below using Dynamic Programming in pseudo-polynomial time, or you can directly use the solution given below – poet_ Feb 25 '19 at 17:20

3 Answers3

2

A possible solution to this is iterating indefinitely until either

  • you have a solution
  • the problem becomes unsolvable given the set of weights

Every iteration step, you subtract the highest possible weight times the highest possible factor, store both in a suitable data structure (my implementation simply uses an Object) and continue.

const plates = [44, 33, 22, 11, 5.5, 2.75];

// We assume that plates is always sorted
const determineWeights = (totalWeight) => {
  let factor = 0;
  let weights = {};

  while (totalWeight > 0) {
    weight = plates.find(weight => Math.floor(totalWeight / weight) > 0);

    // There is no weight we can subtract from the total weight to solve the problem
    // Hence, the problem is unsolvable and we return null to indicate that no solution exists
    if (!weight) { return null; }

    // Determine the factor with which to multiply the weight before we subtract from the total weight an subtract the product
    factor = Math.floor(totalWeight / weight);
    totalWeight = totalWeight - factor * weight;

    // Store weight and factor
    weights[weight] = factor;
  }
  
  return weights;
}


console.log(determineWeights(104.5)); // { "11": 1, "44": 2, "5.5": 1 }
console.log(determineWeights(60.5)); // { "11": 1, "44": 1, "5.5": 1 }
console.log(determineWeights(5.0)); // null

The problem is essentially an instance of the Knapsack problem.

Note that we assume that plates is sorted. Otherwise, Array.find will not necessarily retrieve the maximum weight that can be subtracted from the total weight.

FK82
  • 4,907
  • 4
  • 29
  • 42
1

Here's a solution. In the event that the available plates don't add up to the target weight, it will return the combination of available plates that add up closest to the target. Adapted from this answer.

function createSubsets(numbers, target) {
    // filter out all items larger than target
    numbers = numbers.filter(function (value) {
        return value <= target;
    });

    // sort from largest to smallest
    numbers.sort(function (a, b) {
        return b - a;
    });

    var i;
    var sum = 0;
    var addedIndices = [];

    // go from the largest to the smallest number and
    // add as many of them as long as the sum isn't above target
    for (i = 0; i < numbers.length; i++) {
        if (sum + numbers[i] <= target) {
            sum += numbers[i];
            addedIndices.push(i);
        }
    }

    return addedIndices.map(n => numbers[n]);
}
1

I have a simple solution below if value of the target weight will always be the sum of the available plates. Assuming the weights array are sorted in a descending order. I loop thought all available weights and will only proceed to the next weight if the total exceeds the total weight you require.

function getWeights(targeWeight) {

    let plates = [44, 33, 22, 11, 5.5, 2.75];

    let totalWeight = 0;

    let neededPlates = [];

    let i = 0;

    while(i < plates.length){

        var pweight = totalWeight + plates[i];

        if (pweight > targeWeight) {
            i++;
            continue;
        }

        totalWeight += plates[i];
        neededPlates.push(plates[i]);
    }

    return neededPlates;
}

console.log(getWeights(60.5)); // [44, 11, 5.5]
console.log(getWeights(104.5)); //[44, 44, 11, 5.5]
ngeksyo
  • 419
  • 3
  • 10