Pay with least change Javascript - javascript

I'm trying to guide my users with making cash payments. Let's say the user has two 200 dollar bills, one 100 dollar bill, one 50 dollar bill, two 10 dollar bills and three 5 dollar bill.
The user wants to pay amount of 61. The function should tell the user to pay with the bills that require the least change. This means the function should tell the user to pay with one 50, one 10 and one 5. The user will receive a change of 4.
function payWithLeastChange(amount, bills) {
let billQuantities = organizeBills(bills);
billQuantities.sort((a, b) => a.value - b.value); // sort bills in ascending order
let result = [];
for (const bill of billQuantities) {
while (amount >= bill.value && bill.quantity > 0) {
let foundBill = result.find((b) => b.value === bill.value);
if (foundBill) {
foundBill.quantity++;
} else {
result.push({ value: bill.value, quantity: 1 });
}
amount -= bill.value;
bill.quantity--;
}
}
if (amount > 0) {
return "Error: Not enough bills to make the payment.";
}
return { payment: result, change: amount };
}
function organizeBills(bills) {
let billQuantities = [];
let billValues = Array.from(new Set(bills));
for (const billValue of billValues) {
billQuantities.push({
value: billValue,
quantity: bills.filter((bill) => bill === billValue).length,
});
}
return billQuantities;
}
let amount = 61;
let bills = [200, 100, 100, 100, 50, 50, 50, 20, 20, 20, 10, 10, 10, 5, 5, 5];
let payment = payWithLeastChange(amount, bills);
console.log(payment);
I call the function like so:
let amount = 61;
let bills = [200, 100, 100, 100, 50, 50, 50, 20, 20, 20, 10, 10, 10, 5, 5, 5];
let payment = payWithLeastChange(amount, bills);
console.log(payment);
I'm expecting an output of
{ payment: [
{ value: 50, quantity: 1 },
{ value: 10, quantity: 1 },
{ value: 5, quantity: 1 }
],
change: 4 }
but instead am getting "Error: Not enough bills to make the payment." I'm stuck and can't figure out what to change in the function.

I think the idea behind the code is faulty to begin with, but I can give you some pointers along the way.
What's messing up your code is the line while (amount >= bill.value && bill.quantity > 0) { where you will only add up bills that are lower than the value. But you can get a change on $100. In fact, below are possible combinations that renders change to $63.
200 (a change of $137)
100
50+50
50+20
50+10+10
50+10+5
50+5+5+5
20+20+20+10
20+20+20+5
20+20+10+10
20+20+10+5
20+10+10+10+5
20+10+10+5+5+5
So what you need is to find all these combinations (and more), and then find a combination that have a) the least amount of change, but also b) the least amount of bills, i.e. 50+10+5.
You don't need organizeBills(). It's better to just work with the bills array, but you will need to loop through that array multiple times in one way or another.

Related

How do I remove a random element from an array only ONCE? I keep getting more than 2 of each letter in my array [duplicate]

JavaScript
I've tried searching for something like this, but I am not able to find it.
It's a simple idea:
a. Take a random number between 0 to 10.
b. Let's say the random number rolled is a 3.
c. Then, save the number (the 3).
d. Now, take another random number again between 0 to 10, but it can't be the 3, because it has already appeared.
One solution is to generate an array (a "bucket") with all the values you want to pick, in this case all numbers from 0 to 10. Then you pick one randomly from the array and remove it from the bucket. Note that the example below doesn't check if the bucket is empty, so if you call the function below more than 10 times you will get an error.
var bucket = [];
for (var i=0;i<=10;i++) {
bucket.push(i);
}
function getRandomFromBucket() {
var randomIndex = Math.floor(Math.random()*bucket.length);
return bucket.splice(randomIndex, 1)[0];
}
// will pick a random number between 0 and 10, and can be called 10 times
console.log(getRandomFromBucket());
using d3:
var bucket = d3.shuffle(d3.range(11));
while(bucket.length) {
console.log(bucket.pop());
}
You can use something like this:
/**
* range Get an array of numbers within a range
* #param min {number} Lowest number in array
* #param max {number} Highest number in array
* #param rand {bool} Shuffle array
* #return {array}
*/
range: function( min, max, rand ) {
var arr = ( new Array( ++max - min ) )
.join('.').split('.')
.map(function( v,i ){ return min + i })
return rand
? arr.map(function( v ) { return [ Math.random(), v ] })
.sort().map(function( v ) { return v[ 1 ] })
: arr
}
And use it like so:
var arr = range( 1, 10, true )
Now you have an array with 10 numbers from 1 to 10 in random order and never repeated. So next you can do this:
arr.forEach(function( num, i ) {
// do something, it will loop 10 times
// and num will always be a different number
// from 1 to 10
});
Just for fun: derived from #Strilles answer a 'bucket constructor'
function RandomBucket(from,until){
min = (Number(from) || 0);
max = (Number(until) || 10)+1;
this.bucket = String(Array(max-min)).split(',').map(function(i){
return min++;
});
if (!RandomBucket.prototype.get){
RandomBucket.prototype.get = function(){
var randomValue =
this.bucket.length < 2
? this.bucket.shift()
: this.bucket.splice(Math.floor(Math.random()*this.bucket.length),1);
return randomValue || 'bucket empty';
};
}
}
See JsFiddle for usage example
Most of the time I'd stick with the method suggested by the other answers - i.e. create an array of possibilities, create a shuffled version of it, then take the first n values as your sample (all operations that are simple and general and can be implemented immutably).
However this isn't great if the range of possibilities is large compared to how much memory you want to use, or compared to how many random values you want to draw (although #Strilles solution uses the memory, but doesn't draw many random values, so is probably the best even for my usecase below).
A solution along the lines your question seems to suggest could look like this:
// select n integers from the range [from, to] (inclusive at both sides),
// don't use this approach for large values of n
// taking random values from the randomSource as needed
function randomNumbersWithoutReplacement(n, from, to, randomSource = Math.random) {
const result = [];
for (let i = 0; i < n; ++i) {
// i values have already been taken
// the +1 makes it inclusive
const rangeWidth = to - from - i + 1
let value = Math.floor(rangeWidth * randomSource()) + from
// correct the value compared to the already sampled integers
for (let j = 0; j < result.length; ++j) {
if (result[j] <= value) {
value++
}
}
result.push(value)
// sorting makes the correction loop simpler
// (and it's nice to report the result sorted too)
result.sort((a, b) => a - b)
}
return result
}
And why might you want this?
const quantumLottoNumbers = randomNumbersWithoutReplacement(6, 1, 59, quantumRandomSource)
Var rnd = getRnd();
While(rnd != lastRnd)
rnd = getRnd();
Where getRnd() is a function that generates your random number.
Actually, you would have to check if your current random number were in an array... And if your list of possible random numbers is small beware of an infinite loop.
Simply use the following function, which will draw a sample between 2 numbers based on sample size, and do so without replacement:
function random_sample_without_replacement(options) {
const arr = [];
while(arr.length < options.sample_size){
var r = Math.floor(Math.random() * options.population_size) + 1;
if(arr.indexOf(r) === -1) {
arr.push(r);
}
}
return(arr)
}
Usage:
random_sample = random_sample_without_replacement({
population_size : 1000,
sample_size : 100
})
[950, 725, 239, 273, 814, 325, 834, 702, 209, 740, 539, 281, 799, 459, 443, 758, 567, 124, 428, 462, 576, 234, 35, 344, 441, 580, 461, 371, 354, 616, 704, 233, 486, 296, 182, 63, 57, 357, 226, 969, 396, 879, 904, 718, 22, 121, 835, 52, 310, 359, 593, 793, 421, 870, 719, 959, 639, 755, 85, 10, 365, 189, 457, 895, 168, 574, 115, 176, 252, 284, 840, 721, 962, 780, 851, 71, 144, 827, 843, 643, 54, 246, 838, 100, 452, 303, 20, 572, 259, 102, 909, 471, 642, 8, 716, 388, 374, 338, 425, 880]
check to see if truly without replacement:
[...new Set(random_sample)].length
100
As a great person (Joma) once said, "Hashmap, I'll use a Hashmap!".
You can simply store the already taken values as object keys, and check every time you take a new one. If it's present you increase it in a loop until it becomes a not taken value. If it reaches the length, set it to zero.
function sample(options, count) {
if (options < count) {
throw new Error(
`Random sample error: can't sample ${count} items without repetition from ${options} options`
);
}
const result = [];
const exclude = {};
for (let i = 0; i < count; i++) {
let index = Math.floor(Math.random() * options);
while (exclude[index]) {
index += 1;
index %= options;
}
exclude[index] = true;
result.push(index);
}
return result;
}
sample(10, 10);
// [8, 4, 6, 5, 7, 9, 0, 1, 3, 2]
sample(10, 3);
// [1, 6, 7]
The computational cost of checking the next index isn't that big cause it uses an object instead of an array.
I don't know if you can determine the needed result size with antecedence, but if not, you can separate the inner for code and the exclude variable. Or even generate the entire sequence and just .pop() new values.
For a large space with picking just two numbers I think you can achieve this without a large array and still uniform probability (and fixed time - no while loop) by picking a number and an offset, something like:
const range = 1000000000;
const firstPick = Math.trunc(Math.random()*range);
// range -1 so the offset won't wrap
const offset= Math.trunc(Math.random()*(range-1));
const secondPick = (firstPick+offset)%range;
And for more than this I think you could accumulate the picks in sorted order and then adjust the subsequent picks by how many numbers were skipped past (if memory efficiency and runtime efficiency mattered) - though it would get more complex.

Dynamic Programming Bottoms up approach clarification [duplicate]

This question already has answers here:
What is the difference between bottom-up and top-down?
(9 answers)
Closed 1 year ago.
So I have been really trying to grasp Dynamic Programming. I can say that I really understand the memoization top down approach, but the bottoms up approach is really confusing to me. I was able to solve rods cutting top down, but I had to seek the solution for the bottoms up. I just don't understand when to use a 1D array or a 2D array. Then the for loop within the bottoms up is just confusing. Can anyone help me understand the differences in these two codes conceptually?
// Top Down Memoizaton:
const solveRodCuttingTop = function(lengths, prices, n) {
return solveRodCuttingHelper(0, lengths, prices, n);
};
function solveRodCuttingHelper(idx, span, prices, n, memo = []) {
// BASE CASES
if (idx === span.length || n <= 0 || prices.length !== span.length) {
return 0;
}
let included = 0, excluded = 0;
memo[idx] = memo[idx] || [];
if (memo[idx][n] !== undefined) return memo[idx][n];
if (span[idx] <= n) {
included = prices[idx] + solveRodCuttingHelper(idx, span, prices, n - span[idx], memo);
}
excluded = solveRodCuttingHelper(idx + 1, span, prices, n, memo);
memo[idx][n] = Math.max(included, excluded);
return memo[idx][n];
}
// Bottoms up
const solveRodCuttingBottom = function(lengths, prices, n) {
const rods = Array.from({length: n + 1});
rods[0] = 0;
let maxRevenue = - Infinity;
for (let i = 1; i < rods.length; i++) {
for (let j = 1; j <= i; j++) {
maxRevenue = Math.max(maxRevenue, prices[j - 1] + rods[i - j])
}
rods[i] = maxRevenue
}
return rods[prices.length];
};
const lengths = [1, 2, 3, 4, 5];
const prices = [2, 6, 7, 10, 13];
This is an interesting problem. Maybe I'm over-simplifying it, but if you first calculate each price per length, you can determine the solution by selling as much as possible at the highest rate. If the remaining rod is too short to sell at the best rate, move onto the next best rate and continue.
To solve using this technique, we first implement a createMarket function which takes lenghts and prices as input, and calculates a price-per-length rate. Finally the market is sorted by rate in descending order -
const createMarket = (lengths, prices) =>
lengths.map((l, i) => ({
length: l, // length
price: prices[i], // price
rate: prices[i] / l // price per length
}))
.sort((a, b) => b.rate - a.rate) // sort by price desc
const lengths = [1, 2, 3, 4, 5]
const prices = [2, 6, 7, 10, 13]
console.log(createMarket(lengths, prices))
[
{ length: 2, price: 6, rate: 3 },
{ length: 5, price: 13, rate: 2.6 },
{ length: 4, price: 10, rate: 2.5 },
{ length: 3, price: 7, rate: 2.3333333333333335 },
{ length: 1, price: 2, rate: 2 }
]
Next we write recursive solve to accept a market, [m, ...more], and a rod to cut and sell. The solution, sln, defaults to [] -
const solve = ([m, ...more], rod, sln = []) =>
m == null
? sln
: m.length > rod
? solve(more, rod, sln)
: solve([m, ...more], rod - m.length, [m, ...sln])
const result =
solve(createMarket(lengths, prices), 11)
console.log(result)
[
{ length: 1, price: 2, rate: 2 },
{ length: 2, price: 6, rate: 3 },
{ length: 2, price: 6, rate: 3 },
{ length: 2, price: 6, rate: 3 },
{ length: 2, price: 6, rate: 3 },
{ length: 2, price: 6, rate: 3 }
]
Above, solve returns the rod lengths that sum to the maximum price. If you want the total price, we can reduce the result and sum by price -
const bestPrice =
solve(createMarket(lengths, prices), 11)
.reduce((sum, r) => sum + r.price, 0)
console.log(bestPrice)
32

How to multiply odd index?

I have a little problem with my Javascript code. Here is my question :
I want to write a function, who take in input a table of numbers. If numbers situated in even index, be returned as the way it is. But, if numbers situated in odd index, be return multiplied by his index.
For example :
Input :
[5, 10, 15, 20, 25, 30, 50, 100]
Return :
[5, 10, 15, 60, 25, 150, 50, 700]
So, my code :
function multiplyNum(numbers) {
const multiply = numbers.map(function(number) {
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 == 0) {
return numbers
}
if (numbers[i] % 2 !== 0) {
return numbers
}
});
return multiplyNum
}
You don't need the for loop at all, you can get the index from the map and multiply the odd values:
function multiplyNum(numbers) {
return numbers.map(function(number, index) {
return index % 2 ? number * index : number;
});
}
console.log(multiplyNum([5, 10, 15, 20, 25, 30, 50, 100]));
You could map with a conditional operator as a check.
var array = [5, 10, 15, 20, 25, 30, 50, 100],
result = array.map((v, i) => i % 2 ? v * i : v);
console.log(result);

Vlookup like function in javascript

Actually I have made this on excel using Vlookup but now I am making this on webpage.
I have a input box where user will enter the value
<input class="text" type="text" name="rawScore" onchange="calcpercentile()">
and I have a span of where user can get the result
<span id="percentile"></span>
I have two arrays
var percentile = [10, 20, 30, 40, 50, 60, 70, 80, 90];
var rawScores = [1, 3, 5, 7, 10, 12, 18, 25, 27];
what code should I write that if I write so I get the
input value
(rawScores) (percentile)
1 10
2 20
3 30
4 40
Your example seems wrong. I expect score 1 to map to the 10th percentile, 2 & 3 to the 20th percentile, and 4 to the 30th percentile.
In essence, I think what you're trying to do is: find the array index of the first raw score that is greater than the input, and return the corresponding value from the percentiles array.
The Javascript could look something like this:
var percentiles = [10, 20, 30, 40, 50, 60, 70, 80, 90];
var rawScores = [1, 3, 5, 7, 10, 12, 18, 25, 27];
function map(input) {
let index = rawScores.findIndex(rawScore => rawScore >= input);
return percentiles[index];
}
console.log(map(1));
console.log(map(2));
console.log(map(3));
console.log(map(4));
Note that browser support for Array#findIndex() is limited. If you need wide browser support, a simple loop-based approach might be better:
var percentiles = [10, 20, 30, 40, 50, 60, 70, 80, 90];
var rawScores = [1, 3, 5, 7, 10, 12, 18, 25, 27];
function map(input) {
for (var i = 0; i < rawScores.length; i++) {
if (rawScores[i] >= input) {
return percentiles[i];
}
}
}
console.log(map(1));
console.log(map(2));
console.log(map(3));
console.log(map(4));
you can input text : 1
span display "10"
window.onload = function(){
var percentile = [0,10, 20, 30, 40, 50, 60, 70, 80, 90];
document.getElementById("rawScore").onchange = function () {
var index = document.getElementById("rawScore").value;
document.getElementById("percentile").innerHTML = percentile[index];
}
}
<input class="text" type="text" id="rawScore">
<span id="percentile"></span>
First you sort your dataset of course
const arr = [0,2,5,2,7,3];
const data = arr.sort();
What may help next, is this function to find the index of the closest number.
console.log(findClosestIndex([0, 1, 2, 3.5, 4.5, 5], 4));
// output: 3
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 4));
// output: 4
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 90));
// output: 5
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], -1));
// output: 0
function findClosestIndex(arr, element) {
let from = 0, until = arr.length - 1
while (true) {
const cursor = Math.floor((from + until) / 2);
if (cursor === from) {
const diff1 = element - arr[from];
const diff2 = arr[until] - element;
return diff1 <= diff2 ? from : until;
}
const found = arr[cursor];
if (found === element) return cursor;
if (found > element) {
until = cursor;
} else if (found < element) {
from = cursor;
}
}
}
So, now you know your index and the length of your array. And you have to get a percentile from that. Let's first calculate an exact percentage.
const index = findClosestIndex(data, input);
const pct = index / arr.length;
Turning this percentage into a percentile is a matter of rounding it.
const percentile = (Math.floor(pct/10)+1) * 10;
(PS: I use this function for buying/selling stocks when their current price is in a certain percentile of the daily transaction price rates.)

Recreating a lookup table in Javascript

So I have a spreadsheet for retrieving membership rates, the columns are Age, Duration & Rate. You simply look down the age column to find the age of the customer, then when you find that age you keep heading down to match it to the correct Duration, then in the final column will be the rate. A (very) small version of that might look like this;
Age,Duration,Rate
18,10,1.33
18,11,1.5
18,12,1.8
19,10,1.4
19,11,1.65
19,12,1.88
20,10,1.48
20,11,1.73
20,12,1.98
So someone age 19, duration 11 has a rate of 1.65. Someone age 20 with a duration of 12 has a rate of 1.98 - easy!
My question is two parts, I want to convert this into a web page where someone enters the age and duration to retrieve the rate. I'm pretty sure my best option for this is a two dimensional array like so;
var array = [[18,10,1.33],[18,11,1.5],[18,12,1.8] .. and so on];
are there any better options for this?
The second question is how do I best iterate over a two dimensional array (if that ends up being the best solution)? As I touched upon before I would need to be able to have an iteration that returns the rate based on a two criteria search. I believe this would consist of a two part iteration but iteration is such a weak spot for me that trying to grasp where in the loops to put my iterations is just brain melting. I think it would look something like so;
for (var i = 0; i < array.length; i++){
for (var j = 0; j < array[i].length; j++){
//Do something... I think something like this
If array[][j] == ageImLookingFor && array[][j+1] == durationImLookingFor
then return array[][j+2] (which is the rate)
}
}
Any help, advice or ideas I would be super grateful
A better option than using an array is to use an object (or Map) with properties (keys) that correspond to valid combinations of age and duration, effectively indexing your data by that key:
var list = {
'18_10': { age: 18, duration: 10, rate: 1.33 }
'18_11': { age: 18, duration: 11, rate: 1.5 },
'18_12': { age: 18, duration: 11, rate: 1.8 },
// .. and so on
};
This way you do not have to iterate over an array (cf. your question #2), but given an age and a duration (let's say in variables that have those names), you can write this to get the matching item:
var item = list[age + '_' + duration];
Of course, you should check that age and duration are valid integer numbers and that the item could be undefined when the combination is not known.
Here is a simple snippet (without any checks) you could use to base your web form on. It builds the above mentioned object from an array having the data.
// Data in array -- will be keyed later
var arr = [
{ age: 18, duration: 10, rate: 1.33 },
{ age: 18, duration: 11, rate: 1.5 },
{ age: 18, duration: 12, rate: 1.8 },
{ age: 19, duration: 10, rate: 1.4 },
{ age: 19, duration: 11, rate: 1.65 },
{ age: 19, duration: 12, rate: 1.33 },
{ age: 20, duration: 10, rate: 1.48 },
{ age: 20, duration: 11, rate: 1.73 },
{ age: 20, duration: 12, rate: 1.98 },
];
// Build map, keyed by age/duration. It will look like:
// {
// '18_10': { age: 18, duration: 10, rate: 1.33 },
// '18_11': { age: 18, duration: 11, rate: 1.33 },
// ...etc
// }
mapByAgeDuration = {};
for (var i=0; i < arr.length; i++) {
mapByAgeDuration[arr[i].age + '_' + arr[i].duration] = arr[i];
}
// Fast retrieval function:
function getItemFor(age, duration) {
return mapByAgeDuration[age + '_' + duration];
}
// I/O
var button = document.getElementById('findRate');
var inputAge = document.getElementById('age');
var inputDuration = document.getElementById('duration');
var outputRate = document.getElementById('rate');
button.onclick = function() {
var age = inputAge.value;
var duration = inputDuration.value;
// Retrieve item for this age and duration
var item = getItemFor(age, duration);
// Output rate
outputRate.textContent = item !== undefined ? item.rate
: 'not a valid combination';
}
Age (18 - 20): <input id="age"><br>
Duration (10 - 12): <input id="duration"><br>
<button id="findRate">Find Rate</button><br>
Rate: <span id="rate"></span><br>
Q1: You can use a hash table for your lookup.
var data = [[18, 10, 1.33], [18, 11, 1.5], [18, 12, 1.8], [19, 10, 1.4], [19, 11, 1.65], [19, 12, 1.88], [20, 10, 1.48], [20, 11, 1.73], [20, 12, 1.98]],
object = {};
data.forEach(function (a) {
object[a[0]] = object[a[0]] || {};
object[a[0]][a[1]] = a[2];
});
// usage
document.write(object[19][11] + '<br>');
document.write(object[20][12] + '<br>');
document.write('<pre>' + JSON.stringify(object, 0, 4) + '</pre>');
Q2: A proposal with Array#some()
If you have sorted data, you could insert a short circuit, if the values are greater then needed.
var data = [[18, 10, 1.33], [18, 11, 1.5], [18, 12, 1.8], [19, 10, 1.4], [19, 11, 1.65], [19, 12, 1.88], [20, 10, 1.48], [20, 11, 1.73], [20, 12, 1.98]],
object = {};
function getValue(p1, p2) {
var result;
data.forEach(function (a) {
if (a[0] === p1 && a[1] === p2) {
result = a[2];
return true;
}
// short circuit for not found values
return a[0] > p1;
});
return result;
}
// usage
document.write(getValue(19, 11) + '<br>');
document.write(getValue(20, 12) + '<br>');
Another approach is to leverage on the array.filter function.
You have to reshape your data into an objects array:
var rates = [
{'age':'18','duration':'10','rate':'1.33'},
{'age':'18','duration':'11','rate':'1.5'},
{'age':'19','duration':'12','rate':'1.8'}
];
function filterRate(item){
if(item.age == this.age && item.duration == this.duration)
return item;
}
function getRateByAgeDuration(age, duration){
res = null;
try{
res = rates.filter(filterRate, {'age':age, 'duration':duration})[0].rate;
}
catch(ex){ console.log(ex);}
return res;
}
document.write(getRateByAgeDuration('18', '10'));
It depends. If you use hashes, you will have O(1) time on average, but O(n) on worst case.
If you prefer to optimize the worst case, you can use binary search to achieve O(lg n) both on average and worst cases.
function binarySearch(array, data, from=0, to=array.length) {
if(from >= to) return -1; // not found
var m = Math.floor((from+to)/2);
for(var i=0; i<data.length; ++i) {
if(data[i] < array[m][i]) return binarySearch(array, data, from, m);
if(data[i] > array[m][i]) return binarySearch(array, data, m+1, to);
}
return m;
}
var idx = binarySearch(array, [18,12]);
if(idx > -1) array[idx];

Categories