// UTILS const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); const map = (mapping: { (x: any, index: number): string; (value: any, index: number, array: any[]): {}; }) => (xs: any[]) => xs.map(mapping); const sum = (values: number[]) => values.reduce((total, current) => total + current, 0); const toLines = (text: string) => text.split("\n"); const sort = (sortFunction: (a: any, b: any) => number) => (xs: any[]) => xs.sort(sortFunction); // DOMAIN interface Checkbook { initialBalance: number; checks: Check[]; } interface Check { id: string; amount: number; type: string; } const total = (checks: Check[]) => sum(checks.map(toAmount)); const average = (checks: Check[]) => total(checks) / checks.length; const toAmount = ({ amount }) => amount; const currentBalance = (initialBalance: number, checks: Check[]) => initialBalance - total(checks); const byId = { asc: (a: Check, b: Check) => Number(a.id) - Number(b.id), desc: (a: Check, b: Check) => Number(b.id) - Number(a.id), }; // PARSING const parseId = (text: string) => text; const parseType = (text: string) => text.replace(/[^a-zA-Z0-9_]/, "").replace(/[!}]/, ""); const parseAmount = (text: string) => parseFloat(text); const parseCheck = (text: string): Check => { const [id, type, amount] = text.split(" "); return { id: parseId(id), type: parseType(type), amount: parseAmount(amount), }; }; const parse = (text: string) => { const [initialBalance, ...checks] = toLines(text); return { initialBalance: parseAmount(initialBalance), checks: checks.map(parseCheck), }; }; // HELPERS const displayAmount = (amount: number) => amount.toFixed(2); const col = (...children: string[]) => children.join("\n"); // const row = (...children: string[]) => children.join(" "); // COMPONENTS const InitialBalance = ({ balance }: { balance: number }) => `Original_Balance: ${displayAmount(balance)}`; const Check = ({ check }: { check: Check }) => `${check.id} ${check.type} ${displayAmount(check.amount)}`; const RunningBalance = ({ balance }: { balance: number }) => `Balance ${displayAmount(balance)}`; const CheckRow = ({ check, balance }) => `${Check({ check })} ${RunningBalance({ balance })}`; const TotalExpense = ({ total }: { total: number }) => `Total expense ${displayAmount(total)}`; const AverageExpense = ({ average }: { average: number }) => `Average expense ${displayAmount(average)}`; // APP const Checkbook = ({ checkbook: { initialBalance, checks }, }: { checkbook: Checkbook; }) => { const checkRows: (checks: Check[]) => string[] = pipe( sort(byId.asc), map((check: Check, index: number) => CheckRow({ check, balance: currentBalance(initialBalance, checks.slice(0, index + 1)), // index + 1 means inclusive }), ), ); return col( InitialBalance({ balance: initialBalance }), ...checkRows(checks), TotalExpense({ total: total(checks) }), AverageExpense({ average: average(checks) }), ); }; // WRAPPER export const checkbookBalancer = (text: string) => { const checkbook = parse(text); return Checkbook({ checkbook }); };
Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink.
Hide child comments as well
Confirm
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Discussion on: Daily Challenge #4 - Checkbook Balancing
For further actions, you may consider blocking this person and/or reporting abuse