Formulas to functions
To generate a list of daily transfer amounts, we need a few things:
- A savings goal and a savings period (to calculate
F
) - Our
totalSaved
formula from earliertotalSaved = (dayNumber * (dayNumber + 1)) * F
- Our
transferAmount
formula from earlier-
transferAmount(today) = totalSaved(today) -totalSaved(yesterday)
-
const generateDailyTransferAmounts = (savingsGoal, savingsPeriod) => {
//multiplication factor
const factor = savingsGoal / savingsPeriod / (savingsPeriod + 1)
//totalSaved formula
const calculateTotalSavedByDay = (dayNum) => {
return dayNum * (dayNum+1) * factor
}
//transferAmount formula
const calculateTransferAmount = (dayNum) => {
return calculateTotalSavedByDay(dayNum) - calculateTotalSavedByDay(dayNum-1)
}
...
}
We also need an array containing the numbers 1
to savingsPeriod
to iterate over.
To populate an array with the numbers 1
to savingsPeriod
, we first pass the Array constructor savingsPeriod + 1
, as we know the array will be zero indexed, and we don't want "Day 0". We can make use of the keys()
method, which returns a new array containing the keys for each index of the array, and the spread operator, which allows us to use slice()
on the returned array, removing the leading 0
and giving us an array of length savingsPeriod
containing the numbers 1
to savingsPeriod
.
const days = [...Array(savingsPeriod+1).keys()].slice(1)
We can now use map()
on the days
array, calling our calculateTransferAmount()
function on each day number to calculate the amount to be transferred on each day. We assign the resulting array to transferAmounts
.
const transferAmounts = days.map(dayNum => parseFloat(calculateTransferAmount(dayNum).toFixed(2)))
So far our code looks likes this:
const generateDailyTransferAmounts = (savingsGoal, savingsPeriod) => {
const factor = savingsGoal/savingsPeriod/(savingsPeriod+1)
const calculateTotalSaved = (dayNum) => {
return dayNum * factor * (dayNum+1)
}
const calculateTransferAmount = (dayNum) => {
return calculateTotalSaved(dayNum) - calculateTotalSaved(dayNum-1)
}
//Array of numbers from 1 to savingsPeriod
const days = [...Array(savingsPeriod+1).keys()].slice(1)
//Array of transfer amounts for each day of the savingsPeriod
const transferAmounts = days.map(dayNum => parseFloat(calculateTransferAmount(dayNum).toFixed(2)))
...
}
Making things 'random' (not quite)
As it stands we have an array, transferAmounts
, that is sorted in ascending order — our first transfer will be the smallest, and our last the biggest.
To get the feeling of 'random' amounts leaving our account in the middle of night, we need to shuffle our transferAmounts
. Shuffling – or truly shuffling — appears to be a contentious topic 😅
I have an array like this:
var arr1 = ["a", "b", "c", "d"];
How can I randomize / shuffle it?
As we don't really need the ultimate shuffle here – just enough for it to feel random – we can use a nice clean implementation of the Durstenfeld shuffle (credit to Laurens Holst on Stackoverflow).
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
Our final code, then, for generating a shuffled list of daily transfer amounts, for our chosen savings goal and savings period is as follows:
const generateDailyTransferAmounts = (goal, period) => {
const savingsGoal = goal
const savingsPeriod = period
const factor = savingsGoal/savingsPeriod/(savingsPeriod+1)
const calculateTotalSaved = (dayNum) => {
return dayNum * factor * (dayNum+1)
}
const calculateTransferAmount = (dayNum) => {
return calculateTotalSaved(dayNum) - calculateTotalSaved(dayNum-1)
}
const days = [...Array(savingsPeriod+1).keys()].slice(1)
const transferAmounts = days.map(dayNum => parseFloat(calculateTransferAmount(dayNum).toFixed(2)))
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
return shuffleArray(transferAmounts)
}
module.exports = generateDailyTransferAmounts
We have included an export
statement so this function can be used elsewhere.
Writing our transfer amounts to a file
As we will be using Serverless and AWS Lambda to run our program every day, we need to make our shuffled transfer amounts array accessible in the file system to any Starling API-based function we will write.
This is achieved through a new function, writeDailyTransferAmounts
:
// this allows us to interact with the file system
const fs = require("fs");
//we import our function from earlier
const generateDailyAmounts = require('./generateDailyAmounts')
const writeDailyAmounts = (goal, period) => {
const dailyTransferAmounts = JSON.stringify(generateDailyTransferAmounts(goal,period))
...
}
You'll notice that as well as calling generateDailyTransferAmounts
with our savings goal and savings period, we are using JSON.stringify
on the result. This is because when you try and write an array to a file, it is converted to a string in the process, and you end up with something like this.
var array = [1, 2, 3, 4];
console.log(array.toString());
// 1,2,3,4
And it loses its array functionality in the destination file.
Using JSON.stringify
allows us to turn an array into a string in a way that means it stays functionally intact.
We can now use fs.writeFile()
to write our array to a new file. As we want a Javascript file that can be accessed by our Starling transfer function, we use template literals to write our file content, substituting in our dailyTransferAmounts
array:
const fileContent =
`const dailyAmounts = ${dailyTransferAmounts}
module.exports = dailyAmounts`
Next we write that content to the file system, to a file called dailyAmounts.js
:
fs.writeFile('../dailyTransferAmounts.js', content, function(err) {
if(err) {
console.log(err);
}
else {
console.log("Output saved to dailyAmounts.js");
}
});
The full code for this step is as follows:
const fs = require("fs");
const generateDailyAmounts = require('./generateDailyAmounts')
const writeDailyAmounts = (goal, period) => {
const dailyTransferAmounts = JSON.stringify(generateDailyTransferAmounts(goal,period))
const fileContent =
`const dailyAmounts = ${dailyTransferAmounts}
module.exports = dailyAmounts`
fs.writeFile('./dailyTransferAmounts.js', fileContent, function(err) {
if(err) {
console.log(err);
}
else {
console.log("Output saved to dailyAmounts.js");
}
})
}
module.exports = writeDailyAmounts
Again we include an export
statement so this function can be used elsewhere.
Top comments (2)
really great post and idea!
thanks! More to come 😄