It is not advised to splice an array that is being iterated, and it is the reason why your loop got suck sometimes.
Instead build a new array, so it doesn't affect the iteration on the input array.
If you first sort the second array in descending order, you can then find the first value in that array that fits the amount, and be sure it is the greatest one. For sorting numerically in descending order, you can use sort
with a callback function.
Once you have found the value to subtract, you can use the remainder operator (%
) to determine what is left over after this subtraction.
function breakDown(amounts, coins) {
// Get a sorted copy (descending) of denominations:
coins = [...coins].sort((a, b) => b - a);
const result = []; // The results are stored here
for (let amount of amounts) {
for (let coin of coins) {
if (coin <= amount) {
result.push(amount - amount % coin);
amount %= coin;
}
}
if (amount) result.push(amount); // remainder
}
return result;
}
// Example 1
console.log(breakDown([80, 6], [25, 5]));
// Example 2
console.log(breakDown([100, 90, 6], [100, 75, 15, 5, 1]));
Explanations
The coins are sorted in descending order so that when we search for a fitting coin from left to right, we can be sure that if we find one, it will be the greatest one that fits, not just any. So for instance, if our amount is 7 and we have coins 2 and 5, we don't want to use coin 2 just yet -- we want to use coin 5 first. So if we sort those coins into [5, 2], and then start looking for the first coin that is smaller than our amount, we will be sure to find 5 first. The result would not be as expected if we would have found 2 first.
We can calculate the remainder of a division with the %
operator, and there is a shortcut for when we want to assign that remainder back to the amount: it is the %=
operator. amount %= coin
is short for amount = amount % coin
.
When the inner loop completes, it might be that amount
is not zero, i.e. there is still an amount that remains that cannot be consumed by any available coin. In that case we want to still have that remainder in the result array, so we push it.
Often, the amount
will already be zero when the loop ends. This will be ensured when the smallest coin is the smallest unit of amount one can expect. For instance if the amount is expressed as an integer, and the smallest coin is 1, then there will never be any remainder left when the inner loop has completed. If however the smallest coin would be 2, and we are left with an a amount of 1, then there is no way to reduce that amount to zero, so after the loop ends, we could be stuck with that remainder. And so we have this code to deal with that:
if (amount) result.push(amount)
Floating point
Be careful with using non-integers: floating point arithmetic does not always lead to expected results, because numbers like 0.1 cannot be accurately represented in floating point. You can end up with a non-zero amount after the inner loop finishes, when zero would have been expected. That amount will be tiny like 1e-15, and really indicates there was such an inaccuracy at play.
When calculating with monetary amounts, it is common practice to do that in number of cents, so to make all calculations based on integers. This will give reliable results.