From 448d5ea5861ed50e6e9e9ee7f2969601427b7796 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Wed, 1 Nov 2023 13:58:47 +0100 Subject: [PATCH 1/4] Helper function for calculating expenses Assuming the following expenses: { resident: 'Person 1', amount: 100 }, { resident: 'Person 2', amount: 40 }, { resident: 'Person 3', amount: 60 }, { resident: 'Person 4', amount: 0 }, then Person 2 owes Person 1 $10 Person 4 owes Person 1 $40 Person 4 owes Person 3 $10 --- app/calculateExpenses.tsx | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/calculateExpenses.tsx diff --git a/app/calculateExpenses.tsx b/app/calculateExpenses.tsx new file mode 100644 index 0000000..c8babe3 --- /dev/null +++ b/app/calculateExpenses.tsx @@ -0,0 +1,43 @@ +type Expense = { + resident: string; + amount: number; +}; + +type Transaction = { + from: string; + to: string; + amount: number; +}; + +export const calculateExpenses = (expenses: Expense[]): Transaction[] => { + // Calculate total spent by each resident + const totalSpent: { [name: string]: number } = {}; + expenses.forEach((expense) => { + totalSpent[expense.resident] = (totalSpent[expense.resident] || 0) + expense.amount; + }); + + // Calculate the average amount spent by all residents + const numResidents = Object.keys(totalSpent).length; + const totalAmountSpent = Object.values(totalSpent).reduce((total, amount) => total + amount, 0); + const averageAmount = totalAmountSpent / numResidents; + + // Calculate who owes or is owed how much + const transactions: Transaction[] = []; + Object.keys(totalSpent).forEach((creditor) => { + let amountOwed = totalSpent[creditor] - averageAmount; + + if (amountOwed > 0) { + Object.keys(totalSpent).forEach((debitor) => { + if (creditor !== debitor && totalSpent[debitor] < averageAmount) { + const settleAmount = Math.min(amountOwed, averageAmount - totalSpent[debitor]); + transactions.push({ to: creditor, from: debitor, amount: settleAmount }); + totalSpent[creditor] -= settleAmount; + totalSpent[debitor] += settleAmount; + amountOwed -= settleAmount; + } + }); + } + }); + + return transactions; +}; \ No newline at end of file From 1e1d73acfd0293b012640cda567a8a737a292e37 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Wed, 1 Nov 2023 13:59:51 +0100 Subject: [PATCH 2/4] Update router.d.ts --- .expo/types/router.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.expo/types/router.d.ts b/.expo/types/router.d.ts index ea8d92a..9ffdd74 100644 --- a/.expo/types/router.d.ts +++ b/.expo/types/router.d.ts @@ -7,7 +7,7 @@ declare module "expo-router" { export * from 'expo-router/build'; // prettier-ignore - type StaticRoutes = `/` | `/(tabs)/_layout` | `/_layout` | `/(tabs)/` | `/(tabs)` | `/(tabs)/two` | `/two` | `/+html` | `/modal`; + type StaticRoutes = `/` | `/(tabs)/` | `/(tabs)` | `/(tabs)/two` | `/two` | `/(tabs)/_layout` | `/_layout` | `/+html` | `/calculateExpenses` | `/modal`; // prettier-ignore type DynamicRoutes = `/${CatchAllRoutePart}`; // prettier-ignore From 923210ebf2fe83d26f94855a6f89ab71f6ba35d9 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 3 Nov 2023 08:50:42 +0100 Subject: [PATCH 3/4] Created helper folder Created new folder in root and moved the calculateExpenses helper function to it --- {app => helpers}/calculateExpenses.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {app => helpers}/calculateExpenses.tsx (100%) diff --git a/app/calculateExpenses.tsx b/helpers/calculateExpenses.tsx similarity index 100% rename from app/calculateExpenses.tsx rename to helpers/calculateExpenses.tsx From 2cabb505e44f730153b025ac6a9d9ebce3e455b8 Mon Sep 17 00:00:00 2001 From: Andreas Date: Fri, 3 Nov 2023 09:11:29 +0100 Subject: [PATCH 4/4] Delete .expo/types/router.d.ts --- .expo/types/router.d.ts | 240 ---------------------------------------- 1 file changed, 240 deletions(-) delete mode 100644 .expo/types/router.d.ts diff --git a/.expo/types/router.d.ts b/.expo/types/router.d.ts deleted file mode 100644 index 9ffdd74..0000000 --- a/.expo/types/router.d.ts +++ /dev/null @@ -1,240 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable import/export */ -/* eslint-disable @typescript-eslint/ban-types */ -declare module "expo-router" { - import type { LinkProps as OriginalLinkProps } from 'expo-router/build/link/Link'; - import type { Router as OriginalRouter } from 'expo-router/src/types'; - export * from 'expo-router/build'; - - // prettier-ignore - type StaticRoutes = `/` | `/(tabs)/` | `/(tabs)` | `/(tabs)/two` | `/two` | `/(tabs)/_layout` | `/_layout` | `/+html` | `/calculateExpenses` | `/modal`; - // prettier-ignore - type DynamicRoutes = `/${CatchAllRoutePart}`; - // prettier-ignore - type DynamicRouteTemplate = `/[...missing]`; - - type RelativePathString = `./${string}` | `../${string}` | '..'; - type AbsoluteRoute = DynamicRouteTemplate | StaticRoutes; - type ExternalPathString = `http${string}`; - type ExpoRouterRoutes = DynamicRouteTemplate | StaticRoutes | RelativePathString; - type AllRoutes = ExpoRouterRoutes | ExternalPathString; - - /**************** - * Route Utils * - ****************/ - - type SearchOrHash = `?${string}` | `#${string}`; - type UnknownInputParams = Record; - type UnknownOutputParams = Record; - - /** - * Return only the RoutePart of a string. If the string has multiple parts return never - * - * string | type - * ---------|------ - * 123 | 123 - * /123/abc | never - * 123?abc | never - * ./123 | never - * /123 | never - * 123/../ | never - */ - type SingleRoutePart = S extends `${string}/${string}` - ? never - : S extends `${string}${SearchOrHash}` - ? never - : S extends '' - ? never - : S extends `(${string})` - ? never - : S extends `[${string}]` - ? never - : S; - - /** - * Return only the CatchAll router part. If the string has search parameters or a hash return never - */ - type CatchAllRoutePart = S extends `${string}${SearchOrHash}` - ? never - : S extends '' - ? never - : S extends `${string}(${string})${string}` - ? never - : S extends `${string}[${string}]${string}` - ? never - : S; - - // type OptionalCatchAllRoutePart = S extends `${string}${SearchOrHash}` ? never : S - - /** - * Return the name of a route parameter - * '[test]' -> 'test' - * 'test' -> never - * '[...test]' -> '...test' - */ - type IsParameter = Part extends `[${infer ParamName}]` ? ParamName : never; - - /** - * Return a union of all parameter names. If there are no names return never - * - * /[test] -> 'test' - * /[abc]/[...def] -> 'abc'|'...def' - */ - type ParameterNames = Path extends `${infer PartA}/${infer PartB}` - ? IsParameter | ParameterNames - : IsParameter; - - /** - * Returns all segements of a route. - * - * /(group)/123/abc/[id]/[...rest] -> ['(group)', '123', 'abc', '[id]', '[...rest]' - */ - type RouteSegments = Path extends `${infer PartA}/${infer PartB}` - ? PartA extends '' | '.' - ? [...RouteSegments] - : [PartA, ...RouteSegments] - : Path extends '' - ? [] - : [Path]; - - /** - * Returns a Record of the routes parameters as strings and CatchAll parameters - * - * There are two versions, input and output, as you can input 'string | number' but - * the output will always be 'string' - * - * /[id]/[...rest] -> { id: string, rest: string[] } - * /no-params -> {} - */ - type InputRouteParams = { - [Key in ParameterNames as Key extends `...${infer Name}` - ? Name - : Key]: Key extends `...${string}` ? (string | number)[] : string | number; - } & UnknownInputParams; - - type OutputRouteParams = { - [Key in ParameterNames as Key extends `...${infer Name}` - ? Name - : Key]: Key extends `...${string}` ? string[] : string; - } & UnknownOutputParams; - - /** - * Returns the search parameters for a route. - */ - export type SearchParams = T extends DynamicRouteTemplate - ? OutputRouteParams - : T extends StaticRoutes - ? never - : UnknownOutputParams; - - /** - * Route is mostly used as part of Href to ensure that a valid route is provided - * - * Given a dynamic route, this will return never. This is helpful for conditional logic - * - * /test -> /test, /test2, etc - * /test/[abc] -> never - * /test/resolve -> /test, /test2, etc - * - * Note that if we provide a value for [abc] then the route is allowed - * - * This is named Route to prevent confusion, as users they will often see it in tooltips - */ - export type Route = T extends string - ? T extends DynamicRouteTemplate - ? never - : - | StaticRoutes - | RelativePathString - | ExternalPathString - | (T extends `${infer P}${SearchOrHash}` - ? P extends DynamicRoutes - ? T - : never - : T extends DynamicRoutes - ? T - : never) - : never; - - /********* - * Href * - *********/ - - export type Href = T extends Record<'pathname', string> ? HrefObject : Route; - - export type HrefObject< - R extends Record<'pathname', string>, - P = R['pathname'] - > = P extends DynamicRouteTemplate - ? { pathname: P; params: InputRouteParams

} - : P extends Route

- ? { pathname: Route

| DynamicRouteTemplate; params?: never | InputRouteParams } - : never; - - /*********************** - * Expo Router Exports * - ***********************/ - - export type Router = Omit & { - /** Navigate to the provided href. */ - push: (href: Href) => void; - /** Navigate to route without appending to the history. */ - replace: (href: Href) => void; - /** Update the current route query params. */ - setParams: (params?: T extends '' ? Record : InputRouteParams) => void; - }; - - /** The imperative router. */ - export const router: Router; - - /************ - * * - ************/ - export interface LinkProps extends OriginalLinkProps { - href: Href; - } - - export interface LinkComponent { - (props: React.PropsWithChildren>): JSX.Element; - /** Helper method to resolve an Href object into a string. */ - resolveHref: (href: Href) => string; - } - - /** - * Component to render link to another route using a path. - * Uses an anchor tag on the web. - * - * @param props.href Absolute path to route (e.g. `/feeds/hot`). - * @param props.replace Should replace the current route without adding to the history. - * @param props.asChild Forward props to child component. Useful for custom buttons. - * @param props.children Child elements to render the content. - */ - export const Link: LinkComponent; - - /** Redirects to the href as soon as the component is mounted. */ - export const Redirect: ( - props: React.PropsWithChildren<{ href: Href }> - ) => JSX.Element; - - /************ - * Hooks * - ************/ - export function useRouter(): Router; - - export function useLocalSearchParams< - T extends AllRoutes | UnknownOutputParams = UnknownOutputParams - >(): T extends AllRoutes ? SearchParams : T; - - /** @deprecated renamed to `useGlobalSearchParams` */ - export function useSearchParams< - T extends AllRoutes | UnknownOutputParams = UnknownOutputParams - >(): T extends AllRoutes ? SearchParams : T; - - export function useGlobalSearchParams< - T extends AllRoutes | UnknownOutputParams = UnknownOutputParams - >(): T extends AllRoutes ? SearchParams : T; - - export function useSegments< - T extends AbsoluteRoute | RouteSegments | RelativePathString - >(): T extends AbsoluteRoute ? RouteSegments : T extends string ? string[] : T; -}