satyacode
Background
Back to js-functions

11-festival-planner.js

JavaScript
1/**
2 * 🎉 Festival Countdown Planner - Module Pattern
3 *
4 * Indian festivals ka planner bana! Module pattern use karna hai —
5 * matlab ek function jo ek object return kare jisme public methods hain,
6 * lekin andar ka data PRIVATE rahe (bahar se directly access na ho sake).
7 *
8 * Function: createFestivalManager()
9 *
10 * Returns an object with these PUBLIC methods:
11 *
12 *   - addFestival(name, date, type)
13 *     date is "YYYY-MM-DD" string, type is "religious"/"national"/"cultural"
14 *     Returns new total count of festivals
15 *     Agar name empty or date not string or invalid type, return -1
16 *     No duplicate names allowed (return -1 if exists)
17 *
18 *   - removeFestival(name)
19 *     Returns true if removed, false if not found
20 *
21 *   - getAll()
22 *     Returns COPY of all festivals array (not the actual private array!)
23 *     Each festival: { name, date, type }
24 *
25 *   - getByType(type)
26 *     Returns filtered array of festivals matching type
27 *
28 *   - getUpcoming(currentDate, n = 3)
29 *     currentDate is "YYYY-MM-DD" string
30 *     Returns next n festivals that have date >= currentDate
31 *     Sorted by date ascending
32 *
33 *   - getCount()
34 *     Returns total number of festivals
35 *
36 * PRIVATE STATE: festivals array should NOT be accessible from outside.
37 *   manager.festivals should be undefined.
38 *   getAll() must return a COPY so modifying it doesn't affect internal state.
39 *   Two managers should be completely independent.
40 *
41 * Hint: This is the Module Pattern — a function that returns an object
42 *   of methods, all closing over shared private variables.
43 *
44 * @example
45 *   const mgr = createFestivalManager();
46 *   mgr.addFestival("Diwali", "2025-10-20", "religious");   // => 1
47 *   mgr.addFestival("Republic Day", "2025-01-26", "national"); // => 2
48 *   mgr.getAll(); // => [{ name: "Diwali", ... }, { name: "Republic Day", ... }]
49 *   mgr.getUpcoming("2025-01-01", 1); // => [{ name: "Republic Day", ... }]
50 */
51export function createFestivalManager() {
52  const festivals = [];
53
54  const VALID_TYPES = ["religious", "national", "cultural"];
55
56  function isValidDate(date) {
57    if (typeof date !== "string") return false;
58    const d = new Date(date);
59    return !isNaN(d.getTime());
60  }
61
62  return {
63
64    addFestival(name, date, type) {
65      if (!name || typeof name !== "string") return -1;
66
67      if (!isValidDate(date)) return -1;
68
69      if (!VALID_TYPES.includes(type)) return -1;
70
71      const exists = festivals.some((f) => f.name === name);
72      if (exists) return -1;
73
74      festivals.push({ name, date, type });
75      return festivals.length;
76    },
77
78    removeFestival(name) {
79      const index = festivals.findIndex((f) => f.name === name);
80      if (index === -1) return false;
81
82      festivals.splice(index, 1);
83      return true;
84    },
85
86    getAll() {
87      return festivals.map((f) => ({ ...f }));
88    },
89
90    getByType(type) {
91      return festivals
92        .filter((f) => f.type === type)
93        .map((f) => ({ ...f }));    
94    },
95
96    getUpcoming(currentDate, n = 3) {
97      return festivals
98        .filter((f) => f.date >= currentDate)   
99        .sort((a, b) => a.date.localeCompare(b.date))
100        .slice(0, n)
101        .map((f) => ({ ...f }));
102    },
103
104    getCount() {
105      return festivals.length;
106    },
107  };
108}