المصفوفات المتقدمة في جافاسكريبت
في الدرس السابق، تعلمت عمليات المصفوفة الأساسية. الآن لنتقدم بـ "حركات القوة" في المصفوفات — أساليب الترتيب الأعلى. هذه تتيح لك فعل المزيد بكود أقل وهي مهارات أساسية في تطوير جافاسكريبت الحديث.
map — تحويل كل عنصر
map تُنفذ دالة على كل عنصر وتُرجع مصفوفة جديدة — الأصل يبقى دون تغيير. فكّر فيها كخط إنتاج: كل عنصر يدخل، يتحول، ويخرج كجزء من منتج جديد.
<script>
let prices = [10, 20, 30];
let doubled = prices.map(p => p * 2); // [20, 40, 60]
</script>
الاستدعاء يُرجع ثلاث وسائط: (element, index, originalArray). في معظم الأوقات تحتاج الأولى فقط.
filter — احتفظ بما يجتاز الاختبار
filter تحتفظ بالعناصر التي تجتاز اختبارًا وتُرجع مصفوفة جديدة — الأصل لا يتغير. مثل نقطة تفتيش: العناصر التي تستوفي المعايير تمر; الباقي يُصفّى.
<script>
let scores = [45, 80, 92, 55, 78, 98];
let passed = scores.filter(s => s >= 60); // [80, 92, 78, 98]
</script>
العناصر التي يُرجع فيها الاستدعاء true تُحتفظ; تلك التي يُرجع false تُهمل.
reduce — تراكم إلى قيمة واحدة
reduce "تضغط" المصفوفة إلى قيمة واحدة. أكثر حالة استخدام شيوعًا هي الجمع:
<script>
let nums = [1, 2, 3, 4, 5];
let sum = nums.reduce((acc, cur) => acc + cur, 0); // 15
</script>
ما تعنيه المعلمات:
acc(المراكم): المجموع الجاري، يُنقل من التكرار السابقcur(الحالي): العنصر الحالي- الوسيط الثاني
0هو القيمة الأولية للمراكم
reduce تبدو صعبة في البداية، لكن جوهرها بسيط: خطوة بخطوة، اضغط المصفوفة إلى قيمة واحدة. الجمع، إيجاد الأعلى، عد التكرارات — reduce تتعامل معها جميعًا.
مثال: دمج map، filter، وreduce
<!DOCTYPE html>
<html>
<body>
<h2>map / filter / reduce</h2>
<div id="output"></div>
<script>
let products = [
{ name: 'لابتوب', price: 999, category: 'إلكترونيات' },
{ name: 'تيشيرت', price: 19, category: 'ملابس' },
{ name: 'ماوس', price: 39, category: 'إلكترونيات' },
{ name: 'بنطلون', price: 49, category: 'ملابس' },
{ name: 'كيبورد', price: 79, category: 'إلكترونيات' }
];
let html = '<strong>جميع المنتجات:</strong><br>';
products.forEach(p => html += `${p.name} - $${p.price} (${p.category})<br>`);
let electronics = products.filter(p => p.category === 'إلكترونيات');
html += '<br><strong>إلكترونيات (filter):</strong><br>';
electronics.forEach(p => html += `${p.name} - $${p.price}<br>`);
let names = products.map(p => p.name);
html += '<br><strong>أسماء المنتجات (map):</strong>' + names.join(', ') + '<br>';
let total = products.reduce((sum, p) => sum + p.price, 0);
html += '<br><strong>السعر الإجمالي (reduce):</strong>$' + total;
document.getElementById('output').innerHTML = html;
</script>
</body>
</html>
find وfindIndex
find— تُرجع أول عنصر يتطابقfindIndex— تُرجع فهرس أول تطابق
إذا لم يُعثر على شيء، find تُرجع undefined وfindIndex تُرجع -1.
<script>
let users = [
{ name: 'سارة', age: 20 },
{ name: 'أحمد', age: 17 },
{ name: 'محمد', age: 25 }
];
users.find(u => u.age > 22); // { name: 'محمد', age: 25 }
users.findIndex(u => u.age < 18); // 1 (فهرس أحمد)
</script>
sort — ترتيب العناصر
sort تُرتّب المصفوفة في المكان (تُعدّل الأصل!). افتراضيًا تُرتّب كنصوص، وهو ما يوقع كثيرًا من المبتدئين:
<script>
let nums = [10, 1, 21, 2];
nums.sort(); // [1, 10, 2, 21] ← ترتيب نصي! "10" < "2"
// الصحيح: الترتيب العددي يحتاج دالة مقارنة
nums.sort((a, b) => a - b); // [1, 2, 10, 21] ← تصاعدي
nums.sort((a, b) => b - a); // [21, 10, 2, 1] ← تنازلي
</script>
sort تُعدّل المصفوفة الأصلية! للحفاظ على الأصل سليمًا، انسخ أولاً: [...arr].sort(...).
مثال: ترتيب درجات الطلاب
<!DOCTYPE html>
<html>
<body>
<h2>ترتيب درجات الطلاب</h2>
<div id="output"></div>
<script>
let students = [
{ name: 'سارة', score: 72 },
{ name: 'أحمد', score: 95 },
{ name: 'محمد', score: 88 },
{ name: 'ليلى', score: 61 },
{ name: 'نور', score: 95 }
];
let html = '<strong>الترتيب الأصلي:</strong><br>';
students.forEach(s => html += `${s.name} - ${s.score}<br>`);
let byScore = [...students].sort((a, b) => b.score - a.score);
html += '<br><strong>ترتيب حسب الدرجة (تنازلي):</strong><br>';
byScore.forEach((s, i) => {
let medal = i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : '';
html += `${medal} ${s.name} - ${s.score}<br>`;
});
let topStudents = students.filter(s => s.score >= 90);
html += '<br><strong>90 فأعلى (filter):</strong><br>';
topStudents.forEach(s => html += `${s.name} - ${s.score}<br>`);
let found = students.find(s => s.name === 'محمد');
html += `<br><strong>إيجاد "محمد" (find):</strong>${found ? found.score : 'لم يُعثر عليه'}`;
document.getElementById('output').innerHTML = html;
</script>
</body>
</html>
some وevery
some— هل عنصر واحد على الأقل يجتاز الاختبار؟ (تُرجع قيمة منطقية)every— هل جميع العناصر تجتاز الاختبار؟ (تُرجع قيمة منطقية)
<script>
let scores = [60, 75, 82, 90];
scores.some(s => s >= 90); // true — شخص حصل على 90+
scores.some(s => s === 100); // false — لم يحصل أحد على درجة كاملة
scores.every(s => s >= 60); // true — الجميع ناجحون
</script>
some تسأل "هل هناك من..."، بينما every تسأل "هل الجميع...". واحدة تتحقق من الوجود; الأخرى تتحقق من الاكتمال.
عامل الانتشار (...)
عامل الانتشار ... "يفتح" المصفوفة. يُستخدم عادةً للنسخ والدمج:
<script>
let a = [1, 2, 3];
let b = [...a]; // نسخ: [1, 2, 3] (مصفوفة جديدة، ليست مرجعًا)
let c = [...a, 4, 5]; // دمج: [1, 2, 3, 4, 5]
let d = [0, ...a, 4]; // إدراج: [0, 1, 2, 3, 4]
let x = [1, 2], y = [3, 4];
let z = [...x, ...y]; // [1, 2, 3, 4]
</script>
إزالة الهيكلة
استخرج القيم من المصفوفة وعيّنها للمتغيرات حسب الموضع:
<script>
let [a, b, c] = [1, 2, 3];
// a = 1, b = 2, c = 3
let [first, ...rest] = [1, 2, 3, 4, 5];
// first = 1, rest = [2, 3, 4, 5]
let [name, score] = ['سارة', 95];
// name = 'سارة', score = 95
</script>
تسلسل الأساليب
أساليب الترتيب الأعلى تُرجع مصفوفات جديدة،所以他 يمكنك تسلسلها واحدًا تلو الآخر:
<script>
let result = students
.filter(s => s.score >= 60) // أولاً، فلترة الطلاب الناجحين
.map(s => s.name) // ثم، استخراج الأسماء
.sort(); // أخيرًا، ترتيب
</script>
مثال: تسلسل لمعالجة البيانات
<!DOCTYPE html>
<html>
<body>
<h2>تسلسل الأساليب</h2>
<div id="output"></div>
<script>
let employees = [
{ name: 'سارة', dept: 'هندسة', salary: 8000 },
{ name: 'أحمد', dept: 'مبيعات', salary: 5000 },
{ name: 'محمد', dept: 'هندسة', salary: 12000 },
{ name: 'ليلى', dept: 'مبيعات', salary: 6500 },
{ name: 'نور', dept: 'هندسة', salary: 10000 },
{ name: 'خالد', dept: 'تسويق', salary: 5500 }
];
let html = '<strong>جميع الموظفين:</strong><br>';
employees.forEach(e => html += `${e.name} | ${e.dept} | $${e.salary}<br>`);
let techHighSalary = employees
.filter(e => e.dept === 'هندسة')
.filter(e => e.salary >= 9000)
.map(e => `${e.name}($${e.salary})`);
html += '<br><strong>موظفو الهندسة ذوو الرواتب العالية (سلسلة filter+map):</strong><br>' + techHighSalary.join(', ');
let avgSalary = employees
.filter(e => e.dept === 'هندسة')
.map(e => e.salary)
.reduce((sum, s) => sum + s, 0);
let count = employees.filter(e => e.dept === 'هندسة').length;
html += `<br><br><strong>متوسط راتب الهندسة:</strong>$${(avgSalary / count).toFixed(0)}`;
let allAbove3k = employees.every(e => e.salary > 3000);
let has12k = employees.some(e => e.salary >= 12000);
html += `<br><br><strong>جميع الرواتب > $3000?</strong>${allAbove3k ? 'نعم' : 'لا'}`;
html += `<br><strong>هل أحد يكسب ≥ $12000?</strong>${has12k ? 'نعم' : 'لا'}`;
document.getElementById('output').innerHTML = html;
</script>
</body>
</html>
مثال: عامل الانتشار وإزالة الهيكلة
<!DOCTYPE html>
<html>
<body>
<h2>عامل الانتشار وإزالة الهيكلة</h2>
<div id="output"></div>
<script>
let a = [1, 2, 3];
let b = [...a];
b.push(4);
let html = `<strong>النسخ بعامل الانتشار:</strong><br>`;
html += `المصفوفة الأصلية a: [${a}]<br>`;
html += `النسخة b: [${b}]<br>`;
html += `تعديل b لا يؤثر على a<br><br>`;
let x = [1, 2], y = [3, 4];
let merged = [...x, ...y, 5];
html += `<strong>المصفوفة المدمجة:</strong>[${merged}]<br><br>`;
let [first, second, ...rest] = [10, 20, 30, 40, 50];
html += `<strong>إزالة الهيكلة:</strong><br>`;
html += `first = ${first}<br>`;
html += `second = ${second}<br>`;
html += `rest = [${rest}]<br><br>`;
let scores = [85, 92, 78, 95, 60];
let [highest, ...others] = [...scores].sort((a, b) => b - a);
html += `<strong>إزالة الهيكلة + ترتيب:</strong><br>`;
html += `الأعلى = ${highest}<br>`;
html += `الآخرون = [${others}]`;
document.getElementById('output').innerHTML = html;
</script>
</body>
</html>
📖 ملخص
mapتُحوّل كل عنصر إلى مصفوفة جديدة،filterتحتفظ بالعناصر المتطابقة،reduceتتراكم إلى قيمة واحدةfindتُرجع أول عنصر متطابق،findIndexتُرجع أول فهرس متطابقsortافتراضيًا تُرتّب نصيًا — للأرقام، مرر دائمًا دالة مقارنة:(a, b) => a - bsomeتتحقق مما إذا كان أي عنصر يتطابق،everyتتحقق مما إذا كانت جميع العناصر تتطابق- عامل الانتشار
...ينسخ ويدمج المصفوفات; إزالة الهيكلة تستخرج العناصر في متغيرات - أساليب الترتيب الأعلى تُرجع مصفوفات جديدة ويمكن تسلسلها لكود مختصر ومقروء
❓ أسئلة شائعة
map وforEach؟map تُرجع مصفوفة جديدة; forEach تُرجع undefined. استخدم map عندما تحتاج النتيجة، forEach عندما تريد فقط تنفيذ إجراء. لا تستخدم push يدويًا داخل forEach — هذا ما map مصممة له.sort تُعدّل المصفوفة الأصلية؟sort عملية في المكان — تُرتّب العناصر بدلاً من إرجاع مصفوفة جديدة. للحفاظ على الأصل، انسخ أولاً: [...arr].sort(...).reduce؟📝 تمارين
- بالنظر إلى المصفوفة
[3, 8, 12, 5, 9, 17, 4]، استخدمfilterللاحتفاظ بالأرقام أكبر من 7، ثمmapلمضاعفتها. أخرج النتيجة. - استخدم
reduceلعد تكرارات كل كلمة في مصفوفة مثل['تفاحة', 'موزة', 'تفاحة', 'كرز', 'موزة', 'تفاحة']→{ تفاحة: 3, موزة: 2, كرز: 1 }. - بالنظر إلى مصفوفة طلاب (مع
nameوscore)، استخدم التسلسل لـ: فلترة الطلاب الناجحين → ترتيب حسب الدرجة تنازليًا → استخراج الأسماء في مصفوفة جديدة. اعرض النتيجة على الصفحة.



