I'm working on a project where I process large chunks of data. In my Javascript I receive this data via Ajax as an array of objects.
An example of this data is:
{ageatdeath: 0, death_count: 4, death_percent: 66.66666666666667}
For every age (ranging from 0 - ~100) I have an object. What I'm trying to do is group those together in ranges of 10 years (e.g. 0-10, 11-20) and create a Pie chart with plotly.
I'm able to do this, but I use a lot of almost duplicate code to achieve this. I'm wondering if there's a 'better' way to achieve this.
Here's my code for the function I'm using now.
function drawPieChartAgeAtDeath(data) {
var range0_10 = null;
var range11_20 = null;
var range21_30 = null;
var range31_40 = null;
var range41_50 = null;
var range51_60 = null;
var range61_70 = null;
var range71_80 = null;
var range81_90 = null;
var range90plus = null;
for (var i = 0, len = data.length; i < len; i++) {
if (data[i].ageatdeath >= 0 && data[i].ageatdeath <= 10) {
range0_10 += data[i].death_percent;
} else if (data[i].ageatdeath >= 11 && data[i].ageatdeath <= 20) {
range11_20 += data[i].death_percent;
} else if (data[i].ageatdeath >= 21 && data[i].ageatdeath <= 30) {
range21_30 += data[i].death_percent;
} else if (data[i].ageatdeath >= 31 && data[i].ageatdeath <= 40) {
range31_40 += data[i].death_percent;
} else if (data[i].ageatdeath >= 41 && data[i].ageatdeath <= 50) {
range41_50 += data[i].death_percent;
} else if (data[i].ageatdeath >= 51 && data[i].ageatdeath <= 60) {
range51_60 += data[i].death_percent;
} else if (data[i].ageatdeath >= 61 && data[i].ageatdeath <= 70) {
range61_70 += data[i].death_percent;
} else if (data[i].ageatdeath >= 71 && data[i].ageatdeath <= 80) {
range71_80 += data[i].death_percent;
} else if (data[i].ageatdeath >= 81 && data[i].ageatdeath <= 90) {
range81_90 += data[i].death_percent;
} else if (data[i].ageatdeath >= 91) {
range90plus += data[i].death_percent;
}
}
var pie_data = [{
values: [range0_10,
range11_20,
range21_30,
range31_40,
range41_50,
range51_60,
range61_70,
range71_80,
range81_90,
range90plus],
labels: ['0-10 year',
'11-20 year',
'21-30 year',
'31-40 year',
'41-50 year',
'51-60 year',
'61-70 year',
'71-80 year',
'81-90 year',
'> 90 year'],
type: 'pie'
}];
var layout = {
height: 400,
width: 500
};
Plotly.newPlot('pieChartDiv', pie_data, layout);
Any help would be really appreciated!
Dividing the age by 10, and flooring it will group things into the buckets you desire. You'll need to offset by 1 as groupings go from 0-10, then 11-20, (as opposed to 0-9, 10-19). Below will automatically generate new buckets greater than 10 if ages exceed 100. Buckets may be empty if there is no data for the age range.
function drawPieChartAgeAtDeath(data) {
var values = [];
for (var i = 0, len = data.length; i < len; i++) {
let valueBucket = Math.floor(Math.max(data[i].ageatdeath-1,0)/10);
values[valueBucket] = values[valueBucket] || 0;
values[valueBucket] += data[i].death_percent;
}
var pie_data = [{
values: values,
labels: ['0-10 year',
'11-20 year',
'21-30 year',
'31-40 year',
'41-50 year',
'51-60 year',
'61-70 year',
'71-80 year',
'81-90 year',
'> 90 year'],
type: 'pie'
}];
var layout = {
height: 400,
width: 500
};
Plotly.newPlot('pieChartDiv', pie_data, layout);
}
You can generate the ranges much more elegantly like this:
const ranges = Array.from({ length: 10 }, () => 0);
data.forEach(item => {
const initRangeIndex = Math.floor((item.ageatdeath - 1) / 10);
const rangeIndex = initRangeIndex === -1 ? 0 :
initRangeIndex > 9 ? 9
: initRangeIndex;
ranges[rangeIndex] += item.death_percent;
});
const pieData = [{
values: ranges,
// ...
Related
the code should be able to handle any amount up to 20000, For example, suppose the Entered amount is 2600 when the balance in the card is 3000. Will output following :
New Balance - 400
Notes:
2000 * 1
500 * 1
100 * 1
(only three banknotes 2000, 500, 100) and the cash limit is 20000
I am new in the javascript world, and I am not able to write the code, could anyone help me out??? please!
var h = 5;
var f = 2;
var t = 1;
var ifAmtLessThn2000 = ifAmtLessThn2000(n) {
var temp;
if (n < 500) {
h += (n / 100);
return {
h
}
} else if (n >= 500 && n < 2000) {
f += n / 500;
h += (n - 500) / 100;
return {
h,
f
}
} else {
temp = n - 1500;
if (temp < 500) {
h += (temp / 100);
return {
h
}
console.log('hundred : ' + h);
} else {
f += 1;
h += (temp - 500) / 100;
console.log('five hundred : ' + f);
console.log('hundred : ' + h);
return {
f,
h
}
}
}
}
var ifAmtGreaterthan2000 = (n) => {
var h = 0;
var f = 0;
var t = 0;
var tt = 0;
var temp;
if (n < 2000) {
tt += (n / 2000);
}
else if (n >= 2000 && n < 10000) {
f += n / 500;
h += (n - 500) / 100;
}
else {
temp = n - 1500;
if (temp < 500) {
h += (temp / 100);
}
else {
f += 1;
h += (temp - 500) / 100;
}
}
}
var checkAmt = (n) => {
if (n < 100 || (n % 100) > 0) {
console.log("Invalid Amount : less than 100 ");
} else {
if (n > 20000) {
console.log("ATM Cash Limit exceeds.");
} else {
if (n <= 2500) {
ifAmtLessThn2500(n);
console.log(h + " x 100");
console.log(f + " x 500");
} else {
temp = n - 2500;
t += temp / 1000;
if (temp > 500)
temp = temp - (1000 * (t - 1));
ifAmtLessThn2500(temp);
console.log(h + " x 100");
console.log(f + " x 500");
console.log(t + " x 1000");
}
}
}
}
checkAmt(2500);
Sorry for a dumb program, but I need help please can anyone give me a solution in typeScript code, returning the req denomination in array!!
const withdraw = (amount) => {
let hundredNotes = 0;
let fiftyNotes = 0;
let twentyNotes = 0;
while (amount >= 20) {
if (
amount >= 100 &&
((amount % 100) % 50 === 0 || (amount % 100) % 20 === 0)
) {
amount -= 100;
hundredNotes++;
} else if (
amount >= 50 &&
((amount % 50) % 100 === 0 || (amount % 50) % 20 === 0)
) {
amount -= 50;
fiftyNotes++;
} else {
amount -= 20;
twentyNotes++;
}
}
return [hundredNotes, fiftyNotes, twentyNotes];
};
console.log(withdraw(230));
console.log(withdraw(250));
amtArray = [2000, 500, 100]; // the denomination you want to find.
for (let i = 0; i < this.amtArray.length; i++) {
this.resultArray.push(Math.floor(total / this.amtArray[i]));
// Get the new total
total = total % this.amtArray[i];
}
var twothousands_notes = this.resultArray[0];
var fivehundred_notes = this.resultArray[1];
var hundred_notes = this.resultArray[2];
console.log('calculated amt : ' + '100 : ' +
hundred_notes + ' 500 : ' +
fivehundred_notes + ' 2000 : ' +
twothousands_notes);
Based on the amount you can adjust the logic.
Hope this helps.. :)
this would cover all your cases
function dispenseCase (inputAmount) {
var notes = [];
var balance = 3000;
if(inputAmount !== 0 && inputAmount % 100 == 0 && inputAmount <= balance) {
var notes2000 = Math.round(inputAmount / 2000);
var notes500 = Math.round((inputAmount - (notes2000 * 2000)) / 500 );
var notes100 = Math.round((inputAmount - ((notes2000 * 2000) + (notes500 * 500))) / 100);
notes.push(notes2000);
notes.push(notes500);
notes.push(notes100);
console.log("balance in you account = ", balance - inputAmount);
console.log(notes);
}
else if (inputAmount > balance) {
console.log("Insufficient balance in your account");
}
else if ( inputAmount % 100 != 0 || inputAmount < 100 ) {
console.log( "Invalid amount entered, amount should be multiples of 100");
}
}
dispenseCase(2600);
ATM denomination program in Javascript.
Here, It'll find the minimum number of notes of different denominations that sum the entered amount. Starting from the highest denomination note to the lowest notes.
function countCurrency(amount) {
var notes = [2000, 500, 200, 100];
var noteCounter = [0, 0, 0, 0];
for (var i = 0; i < 4; i++) {
if (amount >= notes[i]) {
noteCounter[i] = Math.floor(amount / notes[i]);
amount = amount - noteCounter[i] * notes[i];
}
}
// Print notes denomination
console.log("Denomination Count:");
for (var j = 0; j < 4; j++) {
if (noteCounter[j] !== 0) {
console.log(notes[j] + " : " + noteCounter[j]);
}
}
}
countCurrency(3300);
Here is the working example
https://codesandbox.io/s/atm-denomination-javascript-o0wb4?file=/src/index.js
this would print the number of notes in a 2000, 500, 100 order for the amount you enter
function dispenseCase (inputAmount) {
var notes = [];
if(inputAmount !== 0) {
var notes2000 = Math.round(inputAmount / 2000);
var notes500 = Math.round((inputAmount - (notes2000 * 2000)) / 500 );
var notes100 = Math.round((inputAmount - ((notes2000 * 2000) + (notes500 * 500))) / 100);
notes.push(notes2000);
notes.push(notes500);
notes.push(notes100);
console.log(notes);
}
}
dispenseCase(2600);
hope this helps
//ATM Cash Denominations //Cash Input Value Already been Provided in this method // You may use a input stream method to input a user input value
public class Denominations
{
public static void main(String args[])//throws IOException
{
int notes[]={5000,2000,1000,500,100}; //storing all the denominations in an array
int amount = 27000;
int copy=amount; //Making a copy of the amount
int totalNotes=0,count=0;
System.out.println("\nATM CASH DENOMINATIONS: \n");
for(int i=0;i<5;i++) //Since there are 5 different types of notes, hence we check for each note.
{
count=amount/notes[i]; // counting number of notes[i] notes
if(count!=0) //printing that denomination if the count is not zero
{
System.out.println(notes[i]+"\tx\t"+count+"\t= "+notes[i]*count);
}
totalNotes=totalNotes+count; //finding the total number of notes
amount=amount%notes[i]; //finding the remaining amount whose denomination is to be found
}
System.out.println("--------------------------------");
System.out.println("TOTAL\t\t\t= "+copy); //printing the total amount
System.out.println("--------------------------------");
System.out.println("Total Number of Notes\t= "+totalNotes); //printing the total number of notes
}
}
let sumToDenominate=Math.floor(Math.random() * 100);
let billsValues = [100, 50, 20, 10, 5,1];
let restAfterDenomination = [];
let billsNumber = [];
function denomination(sum, billsValues) {
printInitialValue( sumToDenominate, billsValues);
initializeArray( sumToDenominate, billsValues);
for (let i = 1; i <= billsValues.length; i++) {
if (restAfterDenomination[i - 1] > 0 || restAfterDenomination < billsNumber[i]) {
billsNumber.push(Math.floor(restAfterDenomination[i - 1] / billsValues[i]));
console.log(`bill's number of `, billsValues[i], "=>", billsNumber[i]);
restAfterDenomination.push(restAfterDenomination[i - 1] - (billsNumber[i] * billsValues[i]));
} else {
console.log(`rest is less than smallest bill or equal to 0`);
billsNumber.push(0);
// break;
}
}
}
function printInitialValue(amount, billsValue) {
console.log("Denomination sumToDenominate: ", amount);
console.log("____________");
for (const logEntry of billsValue) {
console.log(logEntry);
}
console.log("__________");
}
function initializeArray(amount, billsValues) {
billsNumber.push(Math.floor(amount / billsValues[0]));
console.log(`bill's number of`, billsValues[0], "=>", billsNumber[0]);
restAfterDenomination.push(amount - (billsNumber[0] * billsValues[0]));
denomination(sumToDenominate,billsValues);
I have an array of time ranges with start & end values.
var timeRanges = [{
start: 120,
end: 140
},{
start: 180,
end: 220
},{
start: 250,
end: 300
}]
Need to check whether the selected range overlap the time range or not. And also the selected range should be in between the timeRange intervals. (Ex: 140-180, 220-250)
var selected = {
start: 150,
end: 170
}
Time interval B 'overlaps' A if:
B starts after A starts but before A finishes.
B starts before A starts and finishes after A starts.
So you can write a function which decides exactly that.
function areOverlapping(A, B) {
if(B.start < A.start) {
return B.finish > A.start;
}
else {
return B.start < A.finish;
}
}
const overlaps = timeRanges.some( range =>
(range.start < selected.start && range.end > selected.start) ||
(range.start < selected.end && range.end > selected.end) ||
(range.start > selected.start && range.end < selected.end)
);
Assuming that your time ranges are sorted, this solution will work. Otherwise, you need to implement time range sorting as well.
function isValidRange(timeRanges, selectedRange) {
var isValid = true;
var minStart = timeRanges[0].start;
var maxEnd = timeRanges[timeRanges.length - 1].end;
if(selectedRange.start < selectedRange.end && selectedRange.start > minStart && selectedRange.end < maxEnd) {
for(var i=0; i<timeRanges.length; i++) {
if((selectedRange.start >= timeRanges[i].start && selectedRange.start <= timeRanges[i].end)
|| (selectedRange.end >= timeRanges[i].start && selectedRange.end <= timeRanges[i].end)) {
isValid = false;
break;
}
else if(i != timeRanges.length - 1) {
if(selectedRange.start > timeRanges[i].end && selectedRange.start < timeRanges[i+1].start) {
if(selectedRange.end < timeRanges[i+1].start) {
break;
}
else {
isValid = false;
break;
}
}
}
}
}
else {
isValid = false;
}
return isValid;
}
var timeRanges = [{
start: 120,
end: 140
},{
start: 180,
end: 220
},{
start: 250,
end: 300
}];
var selected = {
start: 141,
end: 222
};
alert(isValidRange(timeRanges, selected));
Why don't you run your selection through the array and calculate what you need?
timeRanges.forEach(function(aRange, index)) {
if (selected.start > aRange.start && selected.end < aRange.end)
console.log('Selection falls within the item ' + index):
}
I'm trying to write a function that returns an array of equally chunked up dates and number of days pertaining to those dates. Should there be a remainder of those days they get appended to the array as follow.
Expected outcome:
[{
'startDate' : 20160719 //dates generated from momentjs
'numOfDays': 5
},{
'startDate' : 20160724
'numOfDays': 5
},{
'startDate' : 20160729
'numOfDays': 3
}]
Below is the function I've written in which you can pass in a start date (momentjs), the total number of days (daysToDisplay) and number of days to be divided by (numOfDays).
function buildQueue(startDate, numOfDays, daysToDisplay) {
if (!startDate || !numOfDays || !daysToDisplay) {
throw new Error('All params required!');
}
var num = numOfDays > daysToDisplay ? daysToDisplay : numOfDays;
var div = Math.floor(daysToDisplay / num);
var count = daysToDisplay;
var rem = daysToDisplay % num;
var lastItem;
var i;
var arr = [];
for (i = 0; i <= daysToDisplay; i += num) {
arr.push({
startDate: moment(startDate, 'YYYYMMDD').add(i, 'days').format('YYYYMMDD'),
numOfDays: numOfDays,
count: i
})
if (rem === count) {
break;
}
count -= num;
}
if (count > 0) {
lastItem = arr[arr.length - 1];
var leftover = daysToDisplay - lastItem.count;
arr.push({
startDate: moment(lastItem.startDate, 'YYYYMMDD').add(num, 'days').format('YYYYMMDD'),
numOfDays: rem,
count: leftover + lastItem.count
});
}
return arr;
}
A working example is here (https://jsfiddle.net/zv5ghqpa/1/). The code appears to work in scenarios where daysToDisplay is dividable by more than 2.
When daysToDisplay is only dividable by one, we get an additional item in the returned array basically due to the zero index in the for loop. The expected outcome if I call buildQueue('20160719', 5, 8) should be:
[{
'startDate': 20160719
'numOfDays': 5
}, {
'startDate': 20160724
'numOfDays': 3
}]
Instead its returning:
[{
'startDate': 20160719
'numOfDays': 5
},{
'startDate': 20160724
'numOfDays': 5
}, {
'startDate': 20160729
'numOfDays': 3
}]
I hope i've given enough info... this is really doing my head in.
Thanks in advance!
I think this is code you're looking for:
function buildQueue(startDate, numOfDays, daysToDisplay) {
if (!startDate || !numOfDays || !daysToDisplay) {
throw new Error('All params required!');
}
var num = numOfDays > daysToDisplay ? daysToDisplay : numOfDays;
var div = Math.floor(daysToDisplay / num);
var count = daysToDisplay;
var rem = daysToDisplay % num;
var n = 0;
var i;
var arr = [];
for (i = 0; i <= daysToDisplay; i += num) {
arr.push({
startDate: moment(startDate, 'YYYYMMDD').add(i, 'days').format('YYYYMMDD'),
numOfDays: daysToDisplay % num,
count: i
})
console.log(rem + ' ' + count);
if (rem === count) {
count = 0;
break;
}
count -= num;
}
if (count > 0) {
var leftover = daysToDisplay - arr[arr.length - 1].count;
arr.push({
startDate: moment(arr[arr.length - 1].startDate, 'YYYYMMDD').add(num, 'days').format('YYYYMMDD'),
numOfDays: daysToDisplay % num,
count: leftover + arr[arr.length - 1].count
});
}
return arr;
}
//console.log(buildQueue(moment(), 80, 100));
console.log(buildQueue(moment(), 5, 8));
//console.log(buildQueue(moment(), 15, 100));
//console.log(buildQueue(moment(), 30, 100));
//console.log(buildQueue(moment(), 45, 100));
I think the 'Expected outcome' was correct before you edited the question. I also note the sample code produced a count property that you don't want in the output.
Perhaps this code does what you want?
function buildQueue(startDate, numOfDays, daysToDisplay) {
var arr = []
while (daysToDisplay > 0) {
arr.push({
startDate: moment(startDate, 'YYYYMMDD')
.add(numOfDays * arr.length, 'days')
.format('YYYYMMDD'),
numDays: Math.min(numOfDays, daysToDisplay)
})
daysToDisplay -= numOfDays
}
return arr
}
Assume i have a function checkTime like the one below where i have to check for multiple condition simultaneously.
var result=0;
function checkTime(time1, time2) {
if (time1 >= 0 && time2 <= 0) {
result = 1;
}
else if (time1 >= 0 && time2 <= 1) {
result = 4;
}
else if (time1 >= 2 && time2 <= 3) {
result = 5;
}
else if (time1 >= 4 && time2 <= 6) {
result = 6;
}
else if (time1 >= 7 && time2 <= 9) {
result = 7;
}
else if (time1 >= 11 && time2 <= 12) {
result = 8;
}
else if (time1 >= 13 && time2 <= 15) {
result = 9;
}
else if (time1 >= 16 && time2 <= 17) {
result = 10;
}
else if (time1 >= 19 && time2 <= 20) {
result = 11;
}
return result;
}
(The above given example is hypothetical)
The function i have used totally works,but:
Is there a better method or procedure or formula to replace this?(where it doesnt have to be this lengthy or ugly)
Thanx!
You can use an array to represent all the combination:
tests = [
{ time1: 0, time2: 0, result: 1 },
{ time1: 0: time2: 1, result: 4 },
...
];
for (var i = 0; i < tests.length; i++) {
if (time1 >= tests[i].time1 && time2 <= tests[i].time2) {
return tests[i].result;
}
}
If the code is identical, and only the values change, you could do something like this:
function checkTime(time1, time2) {
[
[0, 0, 0],
[0, 1, 0]
].forEach(function (it) {
if (time1 >= it[0] && time2 <= it[1]) {
return it[2];
}
});
}
Well, first off you have the possibility of an undefined result, so that makes things ambiguous. Should result start at 0 instead? This is an important detail. Second, you seem to be working with boundaries, so it would help to change the <= to < to make this clearer. (If so, the 7-9/11-12 section has a bug.) Third, you have an implicit comparison of time1 and time2, so make that explicit.
var result = 0;
var diff = time2 - time1;
var bounds = [21, 19, 16, 13, 11, 7, 4, 2, 0];
if (diff <= 0) result = 0; // unexpected outcome
else
for (position = 1; position < bounds.length; ++position) {
if (time1 >= bounds[position]) {
if (time2 < bounds[position - 1]) {
result = 3 + (bounds.size - position);
}
break;
}
}
return result;
Other implementations are possible, but it's hard to tell based on your question exactly what problem you're solving.
follow-up
This section of code has a gap:
else if (time1 >= 7 && time2 <= 9) {
result = 7;
}
else if (time1 >= 11 && time2 <= 12) {
result = 8;
}
If time = 10 and time2 = 10, there is no match. It's easy to miss this type of error when you are repeating yourself. Specifying lower and upper bounds for each condition is unnecessary repetition. Since I couldn't see a pattern to the bounds (which could be delegated to a function), I just put the lower bounds into an array and made sure it was sorted descending so that the loop could stop after the first match.
I'm sending the number/string 0.001 to a the function below:
SignificantFigures = 4;
function LimitNumberOfDigits(num) {
var tempStr = "";
if (isNaN(num))
return "\xD8";
else{
if (parseFloat(num) === 0 || (num.toString().indexOf('.') === -1 && parseInt(num) < 9999) || num.toString().length <= 4) {
return num;
}
tempStr = parseFloat(num).toPrecision(SignificantFigures);
if (tempStr.indexOf("e") > -1) {
var startE = tempStr.indexOf("e");
var endE = 0;
for (var i = startE +2 ; i < tempStr.length; i++ ) { // + to ignore e and sign (+ or - )
if(parseInt(tempStr[i], 10) > 0) {
endE = i;
}else {
break;
}
}
if (startE + 2 === endE) {
var pow = tempStr[endE];
} else {
var pow = tempStr.substring(startE +2 ,endE);
}
return tempStr.substring(0,startE) + "*10<sup>"+ pow +"</sup>";
}else {
return parseFloat(num).toPrecision(SignificantFigures);
}
}
}
When im sending 0.2 or even 0.11 im getting like 0.2000 and 0.1100.
The issue here is the toPrecision acts like ToFixed.
Ideas?
EDIT
What i want? simple as that, if a numbers needs to be changed e.g 0.012312312041 it should be 0.0123 , numbers like 0.12 or 28 should stay the same.