patternjavascriptMinor
Basic financial calculation library
Viewed 0 times
financialcalculationlibrarybasic
Problem
I'm trying to build a JavaScript library that does basic financial calculations, such as NPV, IRR, PV, FV, etc. So far I've added the NPV functionality only, and it's passed my test cases. But I want to know if the code overall can be written better.
```
(function () {
/*
This library depends on underscore.js
*/
'use strict';
var root = this, previousFin = root.Fin, Fin = root.Fin = {}, _ = root._;
Fin.VERSION = '0.0.1';
Fin.PRECISION = 4; // floating point precision
/*
Runs Fin.js in noConflict mode and
returns reference to this Fin object
*/
Fin.noConflict = function () {
root.Fin = previousFin;
return this;
};
/*
NPV - Net Present Value
rate = the periodic discount rate
example: If the discount rate is 10% enter 0.1, not 10.
payments = an array or object (keys are the year numbers) of payments
example: [-100, 50, 60] means an initial cash outflow of 100 at time 0,
then cash inflows of 50 at the end of the period one, and 60 at
the end of the period two.
If you pass {0: -100, 2:50}, then the payment at the end of the
year one is assumed to be 0.
*/
Fin.npv = function (rate, payments, precision) {
if (isNaN(rate)) {
/ rate needs to be a number /
return null;
}
if (_.isArray(payments)) {
/ all elements of the array need to be numbers /
if (!_.all(payments, function (elem) { return !isNaN(elem); })) {
return null;
}
} else if (_.isObject(payments)) {
/ all key, value pairs of the object need to be numbers /
if (!_.all(payments, function (key, value) { return !isNaN(key) && !isNaN(value); })) {
return null;
}
} else {
/ payment needs to be either an array or an object /
return null;
}
if (typeof (precision) === 'undefined' || isNaN(precision)) {
precision = this.PRECISION;
}
```
(function () {
/*
This library depends on underscore.js
*/
'use strict';
var root = this, previousFin = root.Fin, Fin = root.Fin = {}, _ = root._;
Fin.VERSION = '0.0.1';
Fin.PRECISION = 4; // floating point precision
/*
Runs Fin.js in noConflict mode and
returns reference to this Fin object
*/
Fin.noConflict = function () {
root.Fin = previousFin;
return this;
};
/*
NPV - Net Present Value
rate = the periodic discount rate
example: If the discount rate is 10% enter 0.1, not 10.
payments = an array or object (keys are the year numbers) of payments
example: [-100, 50, 60] means an initial cash outflow of 100 at time 0,
then cash inflows of 50 at the end of the period one, and 60 at
the end of the period two.
If you pass {0: -100, 2:50}, then the payment at the end of the
year one is assumed to be 0.
*/
Fin.npv = function (rate, payments, precision) {
if (isNaN(rate)) {
/ rate needs to be a number /
return null;
}
if (_.isArray(payments)) {
/ all elements of the array need to be numbers /
if (!_.all(payments, function (elem) { return !isNaN(elem); })) {
return null;
}
} else if (_.isObject(payments)) {
/ all key, value pairs of the object need to be numbers /
if (!_.all(payments, function (key, value) { return !isNaN(key) && !isNaN(value); })) {
return null;
}
} else {
/ payment needs to be either an array or an object /
return null;
}
if (typeof (precision) === 'undefined' || isNaN(precision)) {
precision = this.PRECISION;
}
Solution
-
Avoid getting a reference to the global object ("window") using
-
Try to declare variables at the top of the function.
-
Regarding
-
Regarding
-
-
all those early isNaN checks seem redundant, why not just do the checks in the main loop? Or, better yet, leave out the checks entirely, let the calculation fail, and the function will return NaN by itself, which makes more sense than null or undefined.
Incidentally, these changes will remove the underscore.js dependency.
Avoid getting a reference to the global object ("window") using
this; it won't work in strict mode. If this is only meant to run in the browser, just use window; otherwise you can do this:var root = (1,eval)('this');-
Try to declare variables at the top of the function.
-
Regarding
isArray: Favor feature detection. Does it need to have a length property? A push function?-
Regarding
return null: Just return. This will return undefined, which should be close enough 99.9% of the time.-
typeof (precision) === 'undefined' can simply be !precision in this case.-
all those early isNaN checks seem redundant, why not just do the checks in the main loop? Or, better yet, leave out the checks entirely, let the calculation fail, and the function will return NaN by itself, which makes more sense than null or undefined.
Incidentally, these changes will remove the underscore.js dependency.
(function () {
'use strict';
var root = (1,eval)('this'),
previousFin = root.Fin,
Fin;
root.Fin = Fin = {};
Fin.VERSION = '0.0.1';
Fin.PRECISION = 4; // floating point precision
/*
Runs Fin.js in noConflict mode and
returns reference to this Fin object
*/
Fin.noConflict = function () {
root.Fin = previousFin;
return this;
};
/*
NPV - Net Present Value
rate = the periodic discount rate
example: If the discount rate is 10% enter 0.1, not 10.
payments = an array or object (keys are the year numbers) of payments
example: [-100, 50, 60] means an initial cash outflow of 100 at time 0,
then cash inflows of 50 at the end of the period one, and 60 at
the end of the period two.
If you pass {0: -100, 2:50}, then the payment at the end of the
year one is assumed to be 0.
*/
Fin.npv = function (rate, payments, precision) {
var i, npv = 0;
if (!precision || isNaN(precision)) {
precision = this.PRECISION;
}
for (i in payments) {
if (payments.hasOwnProperty(i)) {
// do the NaN check here if you want.
// if (isNaN(payments[i])) return;
npv += payments[i] / Math.pow((1 + rate), i);
}
}
return npv.toFixed(precision);
};
}());Code Snippets
var root = (1,eval)('this');(function () {
'use strict';
var root = (1,eval)('this'),
previousFin = root.Fin,
Fin;
root.Fin = Fin = {};
Fin.VERSION = '0.0.1';
Fin.PRECISION = 4; // floating point precision
/*
Runs Fin.js in noConflict mode and
returns reference to this Fin object
*/
Fin.noConflict = function () {
root.Fin = previousFin;
return this;
};
/*
NPV - Net Present Value
rate = the periodic discount rate
example: If the discount rate is 10% enter 0.1, not 10.
payments = an array or object (keys are the year numbers) of payments
example: [-100, 50, 60] means an initial cash outflow of 100 at time 0,
then cash inflows of 50 at the end of the period one, and 60 at
the end of the period two.
If you pass {0: -100, 2:50}, then the payment at the end of the
year one is assumed to be 0.
*/
Fin.npv = function (rate, payments, precision) {
var i, npv = 0;
if (!precision || isNaN(precision)) {
precision = this.PRECISION;
}
for (i in payments) {
if (payments.hasOwnProperty(i)) {
// do the NaN check here if you want.
// if (isNaN(payments[i])) return;
npv += payments[i] / Math.pow((1 + rate), i);
}
}
return npv.toFixed(precision);
};
}());Context
StackExchange Code Review Q#8798, answer score: 5
Revisions (0)
No revisions yet.