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}