404 Not Found

404 Not Found


nginx

المصفوفات المتقدمة في جافاسكريبت

في الدرس السابق، تعلمت عمليات المصفوفة الأساسية. الآن لنتقدم بـ "حركات القوة" في المصفوفات — أساليب الترتيب الأعلى. هذه تتيح لك فعل المزيد بكود أقل وهي مهارات أساسية في تطوير جافاسكريبت الحديث.

map — تحويل كل عنصر

map تُنفذ دالة على كل عنصر وتُرجع مصفوفة جديدة — الأصل يبقى دون تغيير. فكّر فيها كخط إنتاج: كل عنصر يدخل، يتحول، ويخرج كجزء من منتج جديد.

HTML
<script>
let prices = [10, 20, 30];
let doubled = prices.map(p => p * 2);   // [20, 40, 60]
</script>

الاستدعاء يُرجع ثلاث وسائط: (element, index, originalArray). في معظم الأوقات تحتاج الأولى فقط.

filter — احتفظ بما يجتاز الاختبار

filter تحتفظ بالعناصر التي تجتاز اختبارًا وتُرجع مصفوفة جديدة — الأصل لا يتغير. مثل نقطة تفتيش: العناصر التي تستوفي المعايير تمر; الباقي يُصفّى.

HTML
<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 "تضغط" المصفوفة إلى قيمة واحدة. أكثر حالة استخدام شيوعًا هي الجمع:

HTML
<script>
let nums = [1, 2, 3, 4, 5];
let sum = nums.reduce((acc, cur) => acc + cur, 0);  // 15
</script>

ما تعنيه المعلمات:

💡 reduce تبدو صعبة في البداية، لكن جوهرها بسيط: خطوة بخطوة، اضغط المصفوفة إلى قيمة واحدة. الجمع، إيجاد الأعلى، عد التكرارات — reduce تتعامل معها جميعًا.

مثال: دمج map، filter، وreduce

HTML
<!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 تُرجع undefined وfindIndex تُرجع -1.

HTML
<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 تُرتّب المصفوفة في المكان (تُعدّل الأصل!). افتراضيًا تُرتّب كنصوص، وهو ما يوقع كثيرًا من المبتدئين:

HTML
<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(...).

مثال: ترتيب درجات الطلاب

HTML
<!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

HTML
<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 تسأل "هل الجميع...". واحدة تتحقق من الوجود; الأخرى تتحقق من الاكتمال.

عامل الانتشار (...)

عامل الانتشار ... "يفتح" المصفوفة. يُستخدم عادةً للنسخ والدمج:

HTML
<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>

إزالة الهيكلة

استخرج القيم من المصفوفة وعيّنها للمتغيرات حسب الموضع:

HTML
<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>
💡 إزالة الهيكلة تبقي الكود مختصرًا. عندما تُرجع الدالة عدة قيم، لفّها في مصفوفة وإزالة الهيكلة هو نمط شائع.

تسلسل الأساليب

أساليب الترتيب الأعلى تُرجع مصفوفات جديدة،所以他 يمكنك تسلسلها واحدًا تلو الآخر:

HTML
<script>
let result = students
  .filter(s => s.score >= 60)     // أولاً، فلترة الطلاب الناجحين
  .map(s => s.name)               // ثم، استخراج الأسماء
  .sort();                         // أخيرًا، ترتيب
</script>

مثال: تسلسل لمعالجة البيانات

HTML
<!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>
▶ جرّب الكود

مثال: عامل الانتشار وإزالة الهيكلة

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>
▶ جرّب الكود

📖 ملخص

  1. map تُحوّل كل عنصر إلى مصفوفة جديدة، filter تحتفظ بالعناصر المتطابقة، reduce تتراكم إلى قيمة واحدة
  2. find تُرجع أول عنصر متطابق، findIndex تُرجع أول فهرس متطابق
  3. sort افتراضيًا تُرتّب نصيًا — للأرقام، مرر دائمًا دالة مقارنة: (a, b) => a - b
  4. some تتحقق مما إذا كان أي عنصر يتطابق، every تتحقق مما إذا كانت جميع العناصر تتطابق
  5. عامل الانتشار ... ينسخ ويدمج المصفوفات; إزالة الهيكلة تستخرج العناصر في متغيرات
  6. أساليب الترتيب الأعلى تُرجع مصفوفات جديدة ويمكن تسلسلها لكود مختصر ومقروء

❓ أسئلة شائعة

س ما الفرق بين map وforEach؟
ج map تُرجع مصفوفة جديدة; forEach تُرجع undefined. استخدم map عندما تحتاج النتيجة، forEach عندما تريد فقط تنفيذ إجراء. لا تستخدم push يدويًا داخل forEach — هذا ما map مصممة له.
س لماذا sort تُعدّل المصفوفة الأصلية؟
ج إنه خيار تصميم تاريخي. sort عملية في المكان — تُرتّب العناصر بدلاً من إرجاع مصفوفة جديدة. للحفاظ على الأصل، انسخ أولاً: [...arr].sort(...).
س هل يمكنني حذف القيمة الأولية في reduce؟
ج نعم — إذا حُذفت، يصبح العنصر الأول هو القيمة الأولية ويبدأ التكرار من الثاني. لكن مصفوفة فارغة بدون قيمة أولية ستُلقي خطأ،所以他 دائمًا واحدة للأمان.

📝 تمارين

  1. بالنظر إلى المصفوفة [3, 8, 12, 5, 9, 17, 4]، استخدم filter للاحتفاظ بالأرقام أكبر من 7، ثم map لمضاعفتها. أخرج النتيجة.
  2. استخدم reduce لعد تكرارات كل كلمة في مصفوفة مثل ['تفاحة', 'موزة', 'تفاحة', 'كرز', 'موزة', 'تفاحة']{ تفاحة: 3, موزة: 2, كرز: 1 }.
  3. بالنظر إلى مصفوفة طلاب (مع name وscore)، استخدم التسلسل لـ: فلترة الطلاب الناجحين → ترتيب حسب الدرجة تنازليًا → استخراج الأسماء في مصفوفة جديدة. اعرض النتيجة على الصفحة.
100%