دوال المصفوفات المتقدمة في JavaScript: find و sort و reduce
في درس المصفوفات تعلمت push و map و filter. لكن المصفوفات في JavaScript تملك دوالاً أخرى كثيرة تختصر عليك حلقات كاملة: ابحث عن أول منتج سعره أقل من 100، وتأكد أن كل الطلاب ناجحون، ورتّب الأسعار تصاعدياً، واحسب إجمالي السلة. في هذا الدرس ستتعلم أهم هذه الدوال، وستجد أنك تستخدمها في كل مشروع.
البيانات التي سنعمل عليها
Section titled “البيانات التي سنعمل عليها”سنستخدم في أغلب الأمثلة مصفوفة منتجات:
const products = [ { name: "قلم", price: 5, inStock: true }, { name: "حقيبة", price: 250, inStock: false }, { name: "دفتر", price: 20, inStock: true }, { name: "آلة حاسبة", price: 120, inStock: true },];البحث عن عنصر: find و findIndex
Section titled “البحث عن عنصر: find و findIndex”filter تُرجع كل العناصر المطابقة في مصفوفة. أما find فتُرجع أول عنصر يطابق الشرط وتتوقف:
const expensive = products.find((p) => p.price > 100);console.log(expensive.name);
const missing = products.find((p) => p.price > 1000);console.log(missing);
const index = products.findIndex((p) => p.name === "دفتر");console.log(index);حقيبةundefined2findتُرجع العنصر نفسه، أوundefinedإذا لم يوجد.findIndexتُرجع رقم العنصر، أو-1إذا لم يوجد.
تخيّل find مثل البحث عن كتاب في الرف: تمرّ على الكتب واحداً واحداً، وتتوقف عند أول كتاب يطابق ما تريد.
هل يوجد؟ includes و some و every
Section titled “هل يوجد؟ includes و some و every”للقيم البسيطة مثل النصوص والأرقام استخدم includes:
const colors = ["أحمر", "أخضر", "أزرق"];console.log(colors.includes("أخضر"));console.log(colors.includes("أصفر"));truefalseوللشروط استخدم some و every:
const anyOut = products.some((p) => !p.inStock);const allCheap = products.every((p) => p.price < 300);const allInStock = products.every((p) => p.inStock);
console.log(anyOut);console.log(allCheap);console.log(allInStock);truetruefalsesome: هل يحقق الشرط عنصر واحد على الأقل؟every: هل تحقق الشرط كل العناصر؟
الترتيب: sort
Section titled “الترتيب: sort”الترتيب في JavaScript فيه فخ مشهور. انظر:
const numbers = [10, 9, 100, 25];numbers.sort();console.log(numbers);[ 10, 100, 25, 9 ]هذا ليس ترتيباً صحيحاً! السبب أن sort بدون معاملات تحوّل العناصر إلى نصوص وترتبها أبجدياً، والنص "100" يأتي قبل "25" لأن 1 قبل 2.
الحل أن تعطيها دالة مقارنة:
numbers.sort((a, b) => a - b);console.log(numbers);
numbers.sort((a, b) => b - a);console.log(numbers);[ 9, 10, 25, 100 ][ 100, 25, 10, 9 ]دالة المقارنة تأخذ عنصرين وتُرجع رقماً: إذا كان سالباً يأتي a أولاً، وإذا كان موجباً يأتي b أولاً. فالصيغة a - b للترتيب التصاعدي، و b - a للتنازلي.
ولترتيب كائنات نقارن إحدى خصائصها. وللنصوص العربية استخدم localeCompare:
const byPrice = [...products].sort((a, b) => a.price - b.price);console.log(byPrice.map((p) => p.name));
const names = ["منى", "أحمد", "سارة"];names.sort((a, b) => a.localeCompare(b, "ar"));console.log(names);[ 'قلم', 'دفتر', 'آلة حاسبة', 'حقيبة' ][ 'أحمد', 'سارة', 'منى' ]التجميع: reduce
Section titled “التجميع: reduce”reduce تأخذ مصفوفة كاملة وتحوّلها إلى قيمة واحدة: مجموع، أو أكبر رقم، أو نص. تخيّلها مثل كرة ثلج تتدحرج: تمرّ على العناصر واحداً واحداً، وتكبر مع كل عنصر.
const prices = [5, 250, 20, 120];const total = prices.reduce((sum, price) => sum + price, 0);console.log(total);395sum: المُجمِّع، وهو النتيجة المتراكمة حتى الآن.price: العنصر الحالي.0: القيمة التي يبدأ بها المُجمِّع.
خطوة بخطوة: يبدأ sum من 0، ثم يصبح 5، ثم 255، ثم 275، ثم 395.
ومع الكائنات نجمع خاصية محددة، ويمكن أن ندمج filter معها:
const stockValue = products .filter((p) => p.inStock) .reduce((sum, p) => sum + p.price, 0);console.log(stockValue);145هذا الأسلوب يسمى سلسلة الدوال (Chaining): ناتج كل دالة يدخل إلى التي بعدها. يُقرأ كجملة: «خذ المنتجات، واختر المتوفر منها، واجمع أسعاره».
القص: slice و splice
Section titled “القص: slice و splice”الاسمان متشابهان والفرق بينهما كبير:
const letters = ["أ", "ب", "ج", "د", "هـ"];
const firstThree = letters.slice(0, 3);console.log(firstThree);console.log(letters.length);
const removed = letters.splice(1, 2);console.log(removed);console.log(letters);[ 'أ', 'ب', 'ج' ]5[ 'ب', 'ج' ][ 'أ', 'د', 'هـ' ]slice(start, end) |
splice(start, count) |
|
|---|---|---|
| ماذا تفعل | تنسخ جزءاً من المصفوفة | تحذف عناصر من المصفوفة نفسها |
| تغيّر الأصل | لا | نعم |
| المعامل الثاني | رقم التوقف، وهو غير داخل في النتيجة | عدد العناصر المحذوفة |
slice(0, 3) نسخت العناصر من الرقم 0 إلى ما قبل الرقم 3، وبقي طول الأصل 5. أما splice(1, 2) فحذفت عنصرين ابتداءً من الرقم 1 من المصفوفة نفسها.
ملخص سريع
Section titled “ملخص سريع”| الدالة | تُرجع | تغيّر الأصل؟ |
|---|---|---|
find |
أول عنصر مطابق أو undefined |
لا |
findIndex |
رقم أول عنصر مطابق أو -1 |
لا |
includes |
true أو false |
لا |
some و every |
true أو false |
لا |
sort |
المصفوفة نفسها بعد ترتيبها | نعم |
reduce |
قيمة واحدة | لا |
slice |
نسخة من جزء | لا |
splice |
العناصر المحذوفة | نعم |
جرّب بنفسك
Section titled “جرّب بنفسك”عدّل الدرجات، واحسب المتوسط، ورتّب الطلاب:
الناتج
جرّب أن تغيّر درجة عمر إلى 60 وشاهد كيف تتغير النتائج. ثم أضف طالباً رابعاً.
أخطاء شائعة
Section titled “أخطاء شائعة”- ترتيب الأرقام بـ sort() بدون دالة مقارنة: تحصل على ترتيب أبجدي خاطئ. اكتب دائماً
(a, b) => a - b. - نسيان القيمة الابتدائية في reduce: إذا كانت المصفوفة فارغة ولم تحدد قيمة ابتدائية يحدث خطأ. اكتب القيمة الابتدائية دائماً.
- الخلط بين slice و splice: الأولى تنسخ ولا تغيّر، والثانية تحذف من الأصل.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما ناتج [10, 9, 100].sort() بدون دالة مقارنة؟
بدون دالة مقارنة تُرتَّب العناصر كنصوص، والنص الذي يبدأ بـ 1 يسبق الذي يبدأ بـ 9.
سؤال
أي دالة تُرجع أول عنصر يحقق الشرط؟
find تتوقف عند أول عنصر مطابق وتُرجعه. أما filter فتُرجع مصفوفة بكل العناصر المطابقة.
الخلاصة
Section titled “الخلاصة”findوfindIndexللبحث عن أول عنصر مطابق، وincludesللقيم البسيطة.someتسأل «هل يوجد واحد؟»، وeveryتسأل «هل الكل؟».sortتحتاج دالة مقارنة للأرقام، وتغيّر المصفوفة الأصلية.reduceتحوّل المصفوفة إلى قيمة واحدة بمُجمِّع وقيمة ابتدائية.sliceتنسخ جزءاً، وspliceتحذف من الأصل.