问题
I am working on some calculations. By user input, calculations should take place and build a table with data.
My first array, calcTable
is working as intended, each row however, represents a month. In my final table, I want a row to represent a year.
So I am currently trying to populate a new array, calcTableShow
that will hold this data. I am having problems with the logic inside the loop that populates calcTableShow
. The logic can be seen in this image: https://i.imgur.com/6qr03f9.png
The colors are showing my desired logic between the two tables (arrays).
Another way of calculating the payment and the interest columns in the calcTableShow
array, could be:
Example with payment:
CalcTableShow.payment(p1) = CalcTable.totPayment(p12)-CalcTable.totPayment(p0)
CalcTableShow.payment(p2) = CalcTable.totPayment(p24)-CalcTable.totPayment(p12)
more...iterations
Same logic with the interest column.
Please take a look at my code. The new code with yearly periods can be found at the bottom of the javascript code, it is currently in a comment.
My main problem currently is how to populate the new array in the correct way.
Also, while typing here at SO, I was thinking that maybe there is a solution with only one loop, that will only create one array (/table)? Would that be a better option perhaps?
I hope this makes sense, thanks in advance,
--Frank
calculate();
//*********************************************
//************ LINK the INPUTS ****************
//*********************************************
// Select all inputs and add event listener "change" (for calc only).
var inputsArray = document.querySelectorAll("input");
inputsArray.forEach(el => el.addEventListener("change", calculate));
//inputsArray.forEach(el => el.addEventListener("input", linkValue));
//"input"... will cause massive lag with high "t" values...
// Select all inputs and add event listener "input" (for linking values only)
var inputsArrayTwo = document.querySelectorAll("input");
inputsArray.forEach(el => el.addEventListener("input", linkValue));
// Link the range with number inputs
function linkValue(e) {
var sibling = e.target.previousElementSibling || e.target.nextElementSibling;
sibling.value = e.target.value;
}
//*********************************************
//************ UPDATE TABLE OBJECT ************
//*********************************************
function calculate() {
//*********************************************
//************ VARS and CALCS *****************
//*********************************************
let P = parseFloat(document.querySelector("#calc_P").value);//principal
let r = document.querySelector("#calc_r").value / 100;//interest
let n = 12;//compounds
let t = parseFloat(document.querySelector("#calc_t").value);//time in years
let A = parseFloat(document.querySelector("#calc_A").value);//deposits
let p = 12;//periods per year (months)
let nper = p * t;//total number of periods
let rate = (1 + r / n) ** (n / p) - 1;// calculation rate
let F = P * (1 + rate) ** nper + (A * ((1 + rate) ** nper - 1)) / rate;//final value
//test-array
let calcTable = [];
calcTable.push({
period: 0,
payment: 0,
totPayment: P,
interest: 0,
totInterest: 0,
balance: P
});
for (let i = 1; i < nper + 1; i++) {
let previous = i - 1;
let interest = calcTable[previous].balance * rate;
calcTable[i] = {
period: i,
payment: A,
totPayment: (P += A),
interest,
totInterest: interest + calcTable[previous].totInterest,
balance: calcTable[previous].balance + A + interest
};
}
/* THESE FUNCTIONS SHOULD LATER APPLY TO THE NEW ARRAY WITH YEARLY PERIODS
function setPrecision(table) {
for (let i = 0; i < table.length; i++) {
for (let key in table[i]) {
if (key === "interest" || key === "totInterest" || key === "balance") {
table[i][key] = table[i][key].toFixed(0);
}
}
}
}
setPrecision(calcTable);
function localeSet(table) {
for (let i = 0; i < table.length; i++) {
for (let key in table[i]) {
if (key === "interest" || key === "totInterest" || key === "balance") {
table[i][key] = new Intl.NumberFormat("fr", {
minimumFractionDigits: 0
}).format(table[i][key]);
} else {
table[i][key] = new Intl.NumberFormat("fr").format(table[i][key]);
}
}
}
}
localeSet(calcTable);
*/
//*********************************************
//************* CREATE HTML TABLE *************
//*********************************************
var tableSelector = "TblCalc";
//call the jsonToTable Function
jsonToTable(calcTable, tableSelector);
function addEl(parent, nodeName, className) {
var element = document.createElement(nodeName);
if (className) element.className = className;
if (parent) parent.appendChild(element);
return element;
}
function addText(parent, text) {
parent.appendChild(document.createTextNode(text));
}
function jsonToTable(json, selector) {
var table = addEl(null, "table", "tbl-container");
var tbody = addEl(table, "tbody");
var thr = addEl(tbody, "tr", "tbl-headers");
//loop through the property names of the first object
for (var propertyName in json[0]) {
addText(addEl(thr, "th"), propertyName);
}
//loop through the array of objects
for (var ind = 0; ind < json.length; ind++) {
var item = json[ind];
var tr = addEl(tbody, "tr", "tbl-rows");
//loop though each of the objects properties
var first = ind != json.length - 1; // no first item for last row
for (var key in item) {
var el = addEl(tr, "td");
if (first) {
// <---- point of interest
el = addEl(el, "div", "tbl-name-container ");
el = addEl(el, "div", "tbl-name-text");
first = false;
}
//append the table data containing the objects property value
addText(el, "" + item[key]);
}
}
var target = document.getElementById(selector);
target.innerHTML = "";
target.appendChild(table);
}
//*********************************************
//******************** INIT *******************
//*********************************************
document.getElementById("finalValue").innerHTML = F.toFixed(0);
document.getElementById("finalValueTwo").innerHTML = calcTable[nper].balance;
document.getElementById("finalTotalPayments").innerHTML =
calcTable[nper].totPayment;
document.getElementById("finalTotalInterest").innerHTML =
calcTable[nper].totInterest;
//
//
//
// NEW ARRAY WITH YEARLY PERIODS
//
//
/*
let calcTableShow = [];
calcTableShow.push({
period: 0,
payment: 0,
totPayment: calcTable[0].totPayment,
interest: 0,
totInterest: 0,
balance: calcTable[0].totPayment
});
for (let i = 1; i < nper / 12 + 1; i++) {
calcTableShow[i] = {
period: i,
payment: calcTable[i + i * 12].totPayment,
totPayment: calcTable[i + i * 12].payment,
interest: for (let i = 1; i < 13; i++) {calcTable[i].interest},
totInterest: calcTable[i + i * 12].totInterest,
balance: calcTable[i + i * 12].balance
};
}
console.log(calcTableShow);
*/
//
//
//
}
<div class="finalValue">Final Value v.1: <span id="finalValue">0</span></div>
<div class="finalValue">Final Value v.2: <span id="finalValueTwo">0</span></div>
<div class="finalValue">Final Total Payments: <span id="finalTotalPayments">0</span></div>
<div class="finalValue">Final Total Interest: <span id="finalTotalInterest">0</span></div>
<!-- INPUTS -->
<div>
P: <input id="calc_P" type="number" value="1000">
<input type="range" value="1000" step="100" max="10000"><br>
</div>
<div>
A: <input id="calc_A" type="number" value="10">
<input type="range" value="10" step="10" max="500"><br>
</div>
<div>
t: <input id="calc_t" type="number" max="50" value="2">
<input type="range" value="2" max="50"><br>
</div>
<div>
r: <input id="calc_r" type="number" value="10">
<input type="range" value="10" step=".1" max="25"><br>
</div>
<!-- TABLE -->
<div id="TblCalc" class="card table-card"></div>
<!-- JS -->
<script type="text/javascript" src="test.js"></script>
EDIT:
After the answer provided by Thomas Ludewig, I did not quite understand his logic. I have tried get this to work for several hours but I can not get it right. With the if (i % 12 == 0)
I am now trying to populate a new array with the yearly results.
I am however bumping into several issues that I am having a hard time figuring out.
Code below:
calculate();
//*********************************************
//************ LINK the INPUTS ****************
//*********************************************
// Select all inputs and add event listener "change" (for calc only).
var inputsArray = document.querySelectorAll("input");
inputsArray.forEach(el => el.addEventListener("change", calculate));
//inputsArray.forEach(el => el.addEventListener("input", linkValue));
//"input"... will cause massive lag with high "t" values...
// Select all inputs and add event listener "input" (for linking values only)
var inputsArrayTwo = document.querySelectorAll("input");
inputsArray.forEach(el => el.addEventListener("input", linkValue));
// Link the range with number inputs
function linkValue(e) {
var sibling = e.target.previousElementSibling || e.target.nextElementSibling;
sibling.value = e.target.value;
}
//*********************************************
//************ UPDATE TABLE OBJECT ************
//*********************************************
function calculate() {
//*********************************************
//************ VARS and CALCS *****************
//*********************************************
let P = parseFloat(document.querySelector("#calc_P").value); // Starting value
let r = document.querySelector("#calc_r").value / 100; // Interest
let n = 12; // Compounds per year
let t = parseFloat(document.querySelector("#calc_t").value); // Time in years
let A = parseFloat(document.querySelector("#calc_A").value); // Deposits per period
let p = 12; // Deposit periods yearly (monthly)
let nper = p * t; // total # periods
let rate = (1 + r / n) ** (n / p) - 1; // interest rate
let F = P * (1 + rate) ** nper + (A * ((1 + rate) ** nper - 1)) / rate; // Final value
//
/* OLD CODE
//
// new array
let calcTable = [];
// push first row [0]
calcTable.push({
period: 0,
payment: 0,
totPayment: P,
interest: 0,
totInterest: 0,
balance: P
});
// add rest of rows, starting at [1]
for (let i = 1; i < nper + 1; i++) {
let previous = i - 1;
let interest = calcTable[previous].balance * rate;
calcTable[i] = {
period: i,
payment: A,
totPayment: (P += A),
interest,
totInterest: interest + calcTable[previous].totInterest,
balance: calcTable[previous].balance + A + interest
};
}
*/
//
// NEW CODE
//
let calcTableNew = [];
calcTableNew.push({
period: 0,
payment: 0,
totPayment: P,
interest: 0,
totInterest: 0,
balance: P
});
for (let i = 1; i < nper / 12 + 1; i++) {
let calcTableInsideLoop = [];
for (let i = 1; i < nper + 1; i++) {
let previous = i - 1;
let interest = calcTableInsideLoop[previous].balance * rate;
calcTableInsideLoop[i] = {
period: i,
payment: A,
totPayment: (P += A),
interest,
totInterest: interest + calcTableInsideLoop[previous].totInterest,
balance: calcTableInsideLoop[previous].balance + A + interest
};
if (i % 12 == 0) {
addDataToYearlyArray();
}
}
function addDataToYearlyArray() {
calcTableNew[i] = {
period: i,
payment:
calcTableInsideLoop[i * 12].totPayment -
calcTableInsideLoop[i * 12 - 12].totPayment,
totPayment: calcTableInsideLoop[i * 12].totPayment,
interest:
calcTableInsideLoop[i * 12].totInterest -
calcTableInsideLoop[i * 12 - 12].totInterest,
totInterest: calcTableInsideLoop[i * 12].totInterest,
balance: calcTableInsideLoop[i * 12].balance
};
}
}
console.log(calcTableNew);
//
/* THESE FUNCTIONS ARE TEMPORARLY COMMENTED OUT, SHOULD APPLY TO THE NEW ARRAY WITH YEARLY PERIODS
//
function setPrecision(table) {
for (let i = 0; i < table.length; i++) {
for (let key in table[i]) {
if (key === "interest" || key === "totInterest" || key === "balance") {
table[i][key] = table[i][key].toFixed(0);
}
}
}
}
setPrecision(calcTable);
function localeSet(table) {
for (let i = 0; i < table.length; i++) {
for (let key in table[i]) {
if (key === "interest" || key === "totInterest" || key === "balance") {
table[i][key] = new Intl.NumberFormat("fr", {
minimumFractionDigits: 0
}).format(table[i][key]);
} else {
table[i][key] = new Intl.NumberFormat("fr").format(table[i][key]);
}
}
}
}
localeSet(calcTable);
*/
//*********************************************
//************* CREATE HTML TABLE *************
//*********************************************
var tableSelector = "TblCalc";
//call the jsonToTable Function
jsonToTable(calcTable, tableSelector);
function addEl(parent, nodeName, className) {
var element = document.createElement(nodeName);
if (className) element.className = className;
if (parent) parent.appendChild(element);
return element;
}
function addText(parent, text) {
parent.appendChild(document.createTextNode(text));
}
function jsonToTable(json, selector) {
var table = addEl(null, "table", "tbl-container");
var tbody = addEl(table, "tbody");
var thr = addEl(tbody, "tr", "tbl-headers");
//loop through the property names of the first object
for (var propertyName in json[0]) {
addText(addEl(thr, "th"), propertyName);
}
//loop through the array of objects
for (var ind = 0; ind < json.length; ind++) {
var item = json[ind];
var tr = addEl(tbody, "tr", "tbl-rows");
//loop though each of the objects properties
var first = ind != json.length - 1; // no first item for last row
for (var key in item) {
var el = addEl(tr, "td");
if (first) {
// <---- point of interest
el = addEl(el, "div", "tbl-name-container ");
el = addEl(el, "div", "tbl-name-text");
first = false;
}
//append the table data containing the objects property value
addText(el, "" + item[key]);
}
}
var target = document.getElementById(selector);
target.innerHTML = "";
target.appendChild(table);
}
//*********************************************
//******************** INIT *******************
//*********************************************
document.getElementById("finalValue").innerHTML = F.toFixed(0);
document.getElementById("finalValueTwo").innerHTML = calcTable[nper].balance;
document.getElementById("finalTotalPayments").innerHTML =
calcTable[nper].totPayment;
document.getElementById("finalTotalInterest").innerHTML =
calcTable[nper].totInterest;
}
<div class="finalValue">Final Value v.1: <span id="finalValue">0</span></div>
<div class="finalValue">Final Value v.2: <span id="finalValueTwo">0</span></div>
<div class="finalValue">Final Total Payments: <span id="finalTotalPayments">0</span></div>
<div class="finalValue">Final Total Interest: <span id="finalTotalInterest">0</span></div>
<!-- INPUTS -->
<div>
P: <input id="calc_P" type="number" value="1000">
<input type="range" value="1000" step="100" max="10000"><br>
</div>
<div>
A: <input id="calc_A" type="number" value="10">
<input type="range" value="10" step="10" max="500"><br>
</div>
<div>
t: <input id="calc_t" type="number" max="50" value="2">
<input type="range" value="2" max="50"><br>
</div>
<div>
r: <input id="calc_r" type="number" value="10">
<input type="range" value="10" step=".1" max="25"><br>
</div>
<!-- TABLE -->
<div id="TblCalc" class="card table-card"></div>
<!-- JS -->
<script type="text/javascript" src="test2.js"></script>
I hope someone could help me in the right direction here. --Frank
EDIT 2 - SOLVED
I found a way to solve my issue with the annual array/table. The code is still a bit messy here and there. But now it is working as intended.
//*********************************************
//************ LINK the INPUTS ****************
//*********************************************
//debugger - delete me
var inputsArrayDebug = document.querySelectorAll("input");
inputsArrayDebug.forEach(el => el.addEventListener("change", debug));
// Select all inputs and add event listener "input" (for linking values only)
var inputsArray = document.querySelectorAll("input");
inputsArray.forEach(el => el.addEventListener("input", linkValue));
// Link the range with number inputs
function linkValue(e) {
var sibling = e.target.previousElementSibling || e.target.nextElementSibling;
sibling.value = e.target.value;
parametersCalculationGet();
tablesInit(para.P);
monthlyTableCalculate();
annualTableCalculate();
}
//*********************************************
//************ CALCULATIONS *******************
//*********************************************
// set up new empty arrays
var monthlyTable;
var annualTable;
// create month and annual table array element
// and push the first row [0]
function tablesInit(P) {
monthlyTable = [];
annualTable = [];
var obj = {};
obj.period = 0;
obj.payment = 0;
obj.totPayment = P;
obj.interest = 0;
obj.totInterest = 0;
obj.balance = P;
monthlyTable.push(obj);
annualTable.push(obj);
}
function monthlyTableAddNewMonth() {
var obj = {};
var previous = monthlyTable.length - 1;
var interest = monthlyTable[previous].balance * para.rate;
// add monthly input datas to month table;
obj.period = monthlyTable.length;
obj.payment = para.A;
obj.totPayment = para.P += para.A;
obj.interest = interest;
obj.totInterest = interest + monthlyTable[previous].totInterest;
obj.balance = monthlyTable[previous].balance + para.A + interest;
monthlyTable.push(obj);
}
function annualTableAddNewYear() {
var obj = {};
var currentAnTable = annualTable.length;
var previous = monthlyTable.length - 1;
// add monthly input datas to month table;
obj.period = currentAnTable;
obj.payment =
monthlyTable[currentAnTable * 12].totPayment -
monthlyTable[currentAnTable * 12 - 12].totPayment;
obj.totPayment = monthlyTable[currentAnTable * 12].totPayment;
obj.interest =
monthlyTable[currentAnTable * 12].totInterest -
monthlyTable[currentAnTable * 12 - 12].totInterest;
obj.totInterest = monthlyTable[currentAnTable * 12].totInterest;
obj.balance = monthlyTable[currentAnTable * 12].balance;
annualTable.push(obj);
}
function parametersCalculationGet() {
//******************************* FIRES VID INPUT
// get calculation parameters and store in object
para = {};
para.P = parseFloat(document.querySelector("#calc_P").value); // Starting value
para.r = document.querySelector("#calc_r").value / 100; // Annual interest
para.n = 12; // Compounds per year
para.t = parseFloat(document.querySelector("#calc_t").value); // Time in years
para.A = parseFloat(document.querySelector("#calc_A").value); // Deposits per period
para.p = 12; // Deposit periods per year
para.nper = para.p * para.t; // total number of periods
para.rate = (1 + para.r / para.n) ** (para.n / para.p) - 1; // calculated interest rate
para.F =
para.P * (1 + para.rate) ** para.nper +
(para.A * ((1 + para.rate) ** para.nper - 1)) / para.rate; // Final value
return para;
}
// update monthly payments and interests
function monthlyTableCalculate() {
// i assume taht hete some parameters for the calculation will be obtained from somewhere
var para = parametersCalculationGet();
for (i = 0; i < para.nper; i++) {
monthlyTableAddNewMonth();
}
}
function annualTableCalculate() {
var para = parametersCalculationGet();
// if (i % 12 == 0) { } nice trick but now not needed anymore
for (i = 0; i < para.t; i++) {
annualTableAddNewYear();
}
}
// Init
// Get parameters
parametersCalculationGet();
// Push first row [0] to monthlyTable and annualTable
tablesInit(para.P);
// Calculate and push rest of rows to monthlyTable
monthlyTableCalculate();
annualTableCalculate();
function debug() {
console.log("monthlyTable");
console.log(monthlyTable);
console.log("annualTable");
console.log(annualTable);
}
console.log("monthlyTable");
console.log(monthlyTable);
console.log("annualTable");
console.log(annualTable);
<div class="finalValue">Final Value v.1: <span id="finalValue">0</span></div>
<div class="finalValue">Final Value v.2: <span id="finalValueTwo">0</span></div>
<div class="finalValue">Final Total Payments: <span id="finalTotalPayments">0</span></div>
<div class="finalValue">Final Total Interest: <span id="finalTotalInterest">0</span></div>
<!-- INPUTS -->
<div>
P: <input id="calc_P" type="number" value="1000">
<input type="range" value="1000" step="100" max="10000"><br>
</div>
<div>
A: <input id="calc_A" type="number" value="10">
<input type="range" value="10" step="10" max="500"><br>
</div>
<div>
t: <input id="calc_t" type="number" max="50" value="2">
<input type="range" value="2" max="50"><br>
</div>
<div>
r: <input id="calc_r" type="number" value="10">
<input type="range" value="10" step=".1" max="25"><br>
</div>
<!-- TABLE -->
<div id="TblCalc" class="card table-card"></div>
<!-- JS -->
<script type="text/javascript" src="test3.js"></script>
回答1:
sigh Ok Ok you are new to js and i wont let you spaghettyfie your first program ... As you can see, simple things can be done simple :) Please check this formulas - otherwise your boat, your villa and wife will be gone ;)
var monthlyTable ;
var annualTable ;
// set up new empty array
// vreate new array element
function monthlyTableInit(P) {
monthlyTable=[];
var obj = {};
obj.period = 0;
obj.payment = 0;
obj.totPayment = P;
obj.interest = 0;
obj.totInterest = 0;
obj.balance = P;
};
function monthlyTableAddNewMonth(payment, totPayment, interest, toInterest, balance) {
var obj = {}
// add monthly input datas to month table;
obj.period = monthlyTable.lenght;
obj.payment = 0;
obj.totPayment = P;
obj.interest = 0;
obj.totInterest = 0;
obj.balance = P;
monthlyTable.push(obj)
};
function parametersCalculationGet() {
//get calculation parameters and store in object
para = {};
para.P = parseFloat(document.querySelector("#calc_P").value); // Starting value
para.r = document.querySelector("#calc_r").value / 100; // Interest
para.n = 12; // Compounds per year
para.t = parseFloat(document.querySelector("#calc_t").value); // Time in years
para.A = parseFloat(document.querySelector("#calc_A").value); // Deposits per period
para.p = 12; // Deposit periods yearly (monthly)
para.nper = p * t; // total # periods
para.rate = (1 + r / n) ** (n / p) - 1; // interest rate
para.F = P * (1 + rate) ** nper + (A * ((1 + rate) ** nper - 1)) / rate; // Final value
return para
}
//update montlys payments and interests
function monthlyTableCalculate {
// i asume taht hete some parameters for the calculation will be obtained from somewhere
var para = parametersCalculationGet();
for(i = 0; i<monthlyTable.length; i++) {
let previous = i - 1;
// this matheematics will be buggy i do not know your algorithm
var obj = {};
obj.period = i;
obj.payment = 0;
obj.interest = 0;
obj.totInterest = 0;
obj.totPayment = P;
obj.balance = P;
obj.interest = calcTableInsideLoop[previous].balance * para.rate;
obj.totInterest = interest + calcTableInsideLoop[previous].totInterest;
obj.payment = para.A;
obj.totPayment = (obj.P += para.A);
obj.interest = 0; // ?
obj.totInterest = obj.interest + monthlyTable[previous].totInterest;
obj.balance: monthlyTable[previous].balance + obj.A + obj.interest;
}
}
function anualTableCreate(startMonth) {
var annualTable =[];
// if (i % 12 == 0) { } nice trick but now not needed anymopre
for(i = startMonth; i<monthlyTable.length; i = i + 12) {// ad start as a offset
annualTable.push(monthlyTable[i]);
}
}
function runCalculation() {
monthlyTableInit(P);
console.log("monthlyTable");
console.log(monthlyTable);
anualTableCreate(0);
console.log("annualTable");
console.log(annualTable);
}
来源:https://stackoverflow.com/questions/57837700/populate-a-new-array-by-looping-through-another-array