Last active
July 1, 2019 19:46
-
-
Save kerrishotts/461a9057d3f40966e953dde3e441639b to your computer and use it in GitHub Desktop.
Dev.to Daily Challenge #4 (July 1, 2019) (See: https://dev.to/thepracticaldev/daily-challenge-4-checkbook-balancing-hei)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const sanitize = str => str.replace(/[^0-9A-Za-z\.\s]/g, ""); | |
const notBlank = str => str !== ""; | |
const extract = str => { | |
const [ line, category, expense ] = str.split(/\s+/); | |
return { line: Number(line), category, expense: Number(expense) }; | |
}; | |
const byLineAndCategory = (a, b) => a.line < b.line | |
? -1 : a.line > b.line | |
? 1 : a.category < b.category | |
? -1 : a.category > b.category | |
? 1 : 0; | |
const balanceReducer = ( | |
{openingBalance, totalExpenses, entries}, | |
{line, category, expense} | |
) => { | |
const newTotal = totalExpenses + expense; | |
const newBalance = openingBalance - newTotal; | |
return { | |
openingBalance, | |
totalExpenses: newTotal, | |
averageExpense: newTotal / (entries.length + 1), | |
entries: [ ...entries, {line, category, expense, balance: newBalance }] | |
} | |
}; | |
const round2 = n => (Math.round(n * 100) / 100) | |
.toLocaleString(undefined, { | |
style: "decimal", | |
minimumFractionDigits: 2, | |
useGrouping: true | |
}); | |
const balanceCheckbook = (checkbook) => { | |
const [openingBalanceStr, ...entries] = | |
sanitize(checkbook) | |
.trim() | |
.split("\n") | |
.filter(notBlank); | |
const openingBalance = Number(openingBalanceStr); | |
const initialState = { | |
openingBalance, | |
entries: [], | |
averageExpense: 0, | |
totalExpenses: 0 | |
}; | |
const report = | |
entries | |
.map(extract) | |
.sort(byLineAndCategory) | |
.reduce( balanceReducer, initialState ); | |
return ` | |
Original Balance: ${round2(report.openingBalance)} | |
${report.entries.map(({line, category, expense, balance}) => | |
`${line} ${category} ${round2(expense)} Balance ${round2(balance)}` | |
).join("\n")} | |
Total Expenses: ${round2(report.totalExpenses)} | |
Average Expense: ${round2(report.averageExpense)} | |
`.trim(); | |
}; | |
const tests = () => { | |
const assert = (fn, expect) => { | |
try { | |
const r = fn(); | |
if (r !== expect) throw new Error(`Expected\n"${expect}"\nGot\n"${r}"`); | |
} catch(err) { | |
throw new Error(`${err.message}`); | |
} | |
}; | |
// check average | |
assert( () => balanceCheckbook(` | |
100.00 | |
100 EntryOne 50.00 | |
101 EntryTwo 50.00 | |
`), `Original Balance: 100.00 | |
100 EntryOne 50.00 Balance 50.00 | |
101 EntryTwo 50.00 Balance 0.00 | |
Total Expenses: 100.00 | |
Average Expense: 50.00` | |
); | |
// blank lines? | |
assert( () => balanceCheckbook(` | |
100.00 | |
100 EntryOne 50.00 | |
101 EntryTwo 50.00 | |
`), `Original Balance: 100.00 | |
100 EntryOne 50.00 Balance 50.00 | |
101 EntryTwo 50.00 Balance 0.00 | |
Total Expenses: 100.00 | |
Average Expense: 50.00` | |
); | |
// Random characters? | |
assert( () => balanceCheckbook(` | |
100.00 | |
100 EntryOne#() 50.00 | |
101 EntryTwo;_ 50.00 | |
`), `Original Balance: 100.00 | |
100 EntryOne 50.00 Balance 50.00 | |
101 EntryTwo 50.00 Balance 0.00 | |
Total Expenses: 100.00 | |
Average Expense: 50.00` | |
); | |
// what if there are no entries? | |
assert( () => balanceCheckbook(` | |
100.00 | |
`), `Original Balance: 100.00 | |
Total Expenses: 0.00 | |
Average Expense: 0.00` | |
); | |
// negative? | |
assert( () => balanceCheckbook(` | |
1000.00 | |
100 Groceries 79.99 | |
103 Transmission 4,999.39 | |
`), `Original Balance: 1,000.00 | |
100 Groceries 79.99 Balance 920.01 | |
103 Transmission 4,999.39 Balance -4,079.38 | |
Total Expenses: 5,079.38 | |
Average Expense: 2,539.69` | |
); | |
} | |
tests(); | |
// example checkbook | |
const checkbook = ` | |
1233.00 | |
125 Hardware;! 24.8?; | |
123 Flowers 93.5 | |
127 Meat 120.90 | |
120 Picture 34.00 | |
124 Gasoline 11.00 | |
123 Photos;! 71.4?; | |
122 Picture 93.5 | |
132 Tires;! 19.00,?; | |
129 Stamps 13.6 | |
129 Fruits{} 17.6 | |
129 Market;! 128.00?; | |
121 Gasoline;! 13.6?; | |
`; | |
console.log(balanceCheckbook(checkbook)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment