مستوى المقال: مبتدئ
Set و Map في JavaScript: نهاية بطء البحث على Array
لو الـ dashboard بتاعك بياخد 4 ثواني يحسب "هل العميل ده في قائمة المشتركين؟" على مليون سجل، JavaScript مش بطيئة. السبب إنك بتستخدم Array.includes() في مكان مفروض فيه Set.has(). التغيير اللي قدامك بيقفل الفجوة دي بسطر كود واحد، وبيوفّر آلاف الأضعاف من زمن التنفيذ بدون لمس باقي تطبيقك.
المشكلة باختصار
الـ Array في JavaScript بيخزّن العناصر زي صف عربيات في موقف. لما تسأل "هل العميل ahmed@haies.com في الصف؟"، JavaScript بتمشي عربية ورا عربية لحد ما تلاقيه أو توصل لآخر صف. على 100 عنصر دي عملية تافهة. على 5 مليون عنصر دي 4.2 ثانية لكل بحث، وفي تطبيق ويب ده بمعنى موت الـ UX.
المثال البسيط: دفتر التليفونات بفهرس
تخيل عندك دفتر تليفونات قديم فيه 1000 اسم. لو حد سألك "هل اسم محمد موجود؟"، عندك طريقتين:
- تقرا الدفتر صفحة صفحة لحد ما تلاقي الاسم. ممكن تاخد دقيقة كاملة لو الاسم في الآخر.
- تستخدم فهرس أبجدي في أول الدفتر. تفتح صفحة "م"، تشوف بسرعة هل محمد موجود ولا لأ. ثانية واحدة بس.
الـ Array هو الدفتر بدون فهرس. الـ Set هو الفهرس. الفرق مش 10% ولا 50% — الفرق ممكن يبقى آلاف الأضعاف لما البيانات تكبر.
الشرح العلمي: Hash Table في 60 ثانية
الـ Set داخليًا مبني على هيكل اسمه Hash Table. لما تضيف عنصر، JavaScript بتمرّره على دالة رياضية (hash function) ترجّع رقم. الرقم ده هو "العنوان" اللي العنصر بيتخزّن فيه في الذاكرة.
لما تسأل بعدها "هل العنصر موجود؟"، JavaScript بتعمل نفس الحسبة، تروح للعنوان مباشرة، وتشوف لو فيه حاجة. خطوة واحدة، مش مليون. ده اللي اسمه في علم الخوارزميات O(1) — زمن ثابت مهما كبر الحجم. الـ Array في المقابل هو O(n) — الزمن بيكبر بنفس نسبة كبر البيانات.
الكود قبل وبعد بأرقام مقاسة
السيناريو: عندك قائمة 5 مليون email لمشتركين في نشرة بريدية، وعايز تتأكد لكل request جديد إن العميل مشترك قبل ما تبعتله محتوى مدفوع.
قبل (Array — بطيء):
const subscribers = loadFromDB(); // مصفوفة 5,000,000 إيميل
const isSubscriber = subscribers.includes("ahmed@haies.com");
// زمن التنفيذ في أسوأ حالة: ~4,200 مللي ثانية
بعد (Set — سريع):