satyacode
Background
Back to js-datatype-intermediate

10-upi-transaction-log.js

JavaScript
1/**
2 * 💸 UPI Transaction Log Analyzer
3 *
4 * Aaj kal sab UPI pe chalta hai! Tujhe ek month ke transactions ka log
5 * milega, aur tujhe pura analysis karna hai - kitna aaya, kitna gaya,
6 * kiski saath zyada transactions hue, etc.
7 *
8 * Rules:
9 *   - transactions is array of objects:
10 *     [{ id: "TXN001", type: "credit"/"debit", amount: 500,
11 *        to: "Rahul", category: "food", date: "2025-01-15" }, ...]
12 *   - Skip transactions where amount is not a positive number
13 *   - Skip transactions where type is not "credit" or "debit"
14 *   - Calculate (on valid transactions only):
15 *     - totalCredit: sum of all "credit" type amounts
16 *     - totalDebit: sum of all "debit" type amounts
17 *     - netBalance: totalCredit - totalDebit
18 *     - transactionCount: total number of valid transactions
19 *     - avgTransaction: Math.round(sum of all valid amounts / transactionCount)
20 *     - highestTransaction: the full transaction object with highest amount
21 *     - categoryBreakdown: object with category as key and total amount as value
22 *       e.g., { food: 1500, travel: 800 } (include both credit and debit)
23 *     - frequentContact: the "to" field value that appears most often
24 *       (if tie, return whichever appears first)
25 *     - allAbove100: boolean, true if every valid transaction amount > 100 (use every)
26 *     - hasLargeTransaction: boolean, true if some valid amount >= 5000 (use some)
27 *   - Hint: Use filter(), reduce(), sort(), find(), every(), some(),
28 *     Object.entries(), Math.round(), typeof
29 *
30 * Validation:
31 *   - Agar transactions array nahi hai ya empty hai, return null
32 *   - Agar after filtering invalid transactions, koi valid nahi bacha, return null
33 *
34 * @param {Array<{ id: string, type: string, amount: number, to: string, category: string, date: string }>} transactions
35 * @returns {{ totalCredit: number, totalDebit: number, netBalance: number, transactionCount: number, avgTransaction: number, highestTransaction: object, categoryBreakdown: object, frequentContact: string, allAbove100: boolean, hasLargeTransaction: boolean } | null}
36 *
37 * @example
38 *   analyzeUPITransactions([
39 *     { id: "T1", type: "credit", amount: 5000, to: "Salary", category: "income", date: "2025-01-01" },
40 *     { id: "T2", type: "debit", amount: 200, to: "Swiggy", category: "food", date: "2025-01-02" },
41 *     { id: "T3", type: "debit", amount: 100, to: "Swiggy", category: "food", date: "2025-01-03" }
42 *   ])
43 *   // => { totalCredit: 5000, totalDebit: 300, netBalance: 4700,
44 *   //      transactionCount: 3, avgTransaction: 1767,
45 *   //      highestTransaction: { id: "T1", ... },
46 *   //      categoryBreakdown: { income: 5000, food: 300 },
47 *   //      frequentContact: "Swiggy", allAbove100: false, hasLargeTransaction: true }
48 */
49export function analyzeUPITransactions(transactions) {
50
51  if (!Array.isArray(transactions) || transactions.length === 0) {
52    return null;
53  }
54
55
56  const validTransaction = transactions.filter(trx => {
57    const validAmount =
58      typeof trx.amount === "number" &&
59      Number.isFinite(trx.amount) &&
60      trx.amount > 0;
61
62    const validType =
63      trx.type === "credit" || trx.type === "debit";
64
65    return validAmount && validType;
66  });
67
68  if (validTransaction.length === 0) {
69    return null;
70  }
71
72
73  const totals = validTransaction.reduce(
74    (acc, trx) => {
75      if (trx.type === "credit") {
76        acc.totalCredit += trx.amount;
77      } else {
78        acc.totalDebit += trx.amount;
79      }
80      return acc;
81    },
82    { totalCredit: 0, totalDebit: 0 }
83  );
84
85  const totalCredit = totals.totalCredit;
86  const totalDebit = totals.totalDebit;
87
88
89  const netBalance = totalCredit - totalDebit;
90
91  const transactionCount = validTransaction.length;
92
93  const avgTransaction = Math.round(
94    (totalCredit + totalDebit) / transactionCount
95  );
96
97  const highestTransaction = validTransaction.reduce((max, trx) =>
98    trx.amount > max.amount ? trx : max
99  );
100
101
102  const categoryBreakdown = validTransaction.reduce((acc, trx) => {
103    acc[trx.category] = (acc[trx.category] || 0) + trx.amount;
104    return acc;
105  }, {});
106
107  const frequency = {};
108  let frequentContact = "";
109  let maxCount = 0;
110
111  for (const trx of validTransaction) {
112    frequency[trx.to] = (frequency[trx.to] || 0) + 1;
113
114    if (frequency[trx.to] > maxCount) {
115      maxCount = frequency[trx.to];
116      frequentContact = trx.to;
117    }
118  }
119
120
121  const allAbove100 = validTransaction.every(
122    trx => trx.amount > 100
123  );
124
125
126  const hasLargeTransaction = validTransaction.some(
127    trx => trx.amount >= 5000
128  );
129
130  return {
131    totalCredit,totalDebit,netBalance,transactionCount,avgTransaction,highestTransaction,categoryBreakdown,frequentContact,allAbove100,hasLargeTransaction
132  };
133}
134