EduFlow
أساسيات JavaScript
⁦0%⁩

الوحدة 3 · العمل مع البيانات

المصفوفات ودوالها اليومية

18 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

تُنشئ المصفوفات وتغيّرها، وتعرف أيّ دوالها تعدّلها في مكانها، وتستبدل الحلقات المكتوبة يدويًا بـ map وfilter وfind وsome وevery لتحويل القوائم والبحث فيها.

  1. 1إنشاء المصفوفات وتغييرها
  2. 2map: حوّل كل عنصر
  3. 3filter: احتفظ ببعض العناصر فقط
  4. 4find وsome وevery: البحث
  5. 5السلسلة: قراءة خطّ معالجة

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

مصاريف Pocket قائمة، ومعظم ميزاته أسئلة حول هذه القائمة. أيّها مبالغ مستردّة؟ كيف تبدو منسّقة بالدولار؟ هل يوجد ما يتجاوز 100 دولار؟ يمكنك الإجابة عن كل هذا بحلقات القسم 2، لكن المصفوفات تأتي مع دوال (methods) تجيب عن كل سؤال منها بسطر واحد مقروء.

إنشاء المصفوفات وتغييرها

المصفوفة قائمة مرتّبة من القيم. وقد رأيت أساسياتها: أقواس مربّعة، ومواضع تبدأ من 0، وlength.

JavaScript
const amounts = [450, 1220, 270];

amounts.push(899);           // add to the end
console.log(amounts);
const last = amounts.pop();  // remove from the end, and get it back
console.log(last, amounts);
amounts[0] = 500;            // replace an item by index
console.log(amounts, amounts.includes(1220), amounts.indexOf(270));

لحظة: amounts مُصرَّح بها بـ const، ومع ذلك تغيّرت ثلاث مرات. ليس في هذا تناقض. const تعني أن الاسم يشير دائمًا إلى المصفوفة نفسها، ولا تقول شيئًا عمّا بداخلها. push وpop والإسناد بالفهرس كلها تغيّر محتويات تلك المصفوفة الواحدة، لذلك هي مسموحة. وحدها amounts = [...]، أي توجيه الاسم إلى مصفوفة مختلفة، هي التي ترمي خطأ.

الاسم amounts المُصرَّح به بـ const يشير إلى مصفوفة واحدة. push تغيّر العناصر داخل تلك المصفوفة، وهذا مسموح. أما توجيه amounts إلى مصفوفة أخرى فهو ما تمنعه const. const amounts [450, 1220, 270, 899] push / pop / [0] = … تغيّر الصندوق: مسموح إعادة توجيه السهم: TypeError
const تثبّت السهم، لا المحتويات

هذه الفكرة، أن الاسم يشير إلى قائمة وأن عدّة عمليات تغيّر القائمة نفسها، هي سبب ضرورة معرفة أيّ الدوال تعدّل في المكان (mutate):

تغيّر المصفوفة نفسها تُرجع شيئًا جديدًا وتترك المصفوفة كما هي
push، pop، shift، unshift slice، concat
splice map، filter
sort، reverse toSorted، toReversed

التعديل في المكان ليس خطأً، لكنه يفاجئ الناس حين يتشارك جزءان من البرنامج مصفوفة واحدة. والدوال في باقي هذا الدرس لا تعدّل في المكان أبدًا، وهذا جزء كبير من سبب شعبيتها.

والثنائي الذي يُربك الجميع هو slice وsplice، يفصل بينهما حرف واحد وسلوكهما متعاكس:

JavaScript
const amounts = [450, 1220, 270, 899];

const firstTwo = amounts.slice(0, 2);   // copy of indexes 0 and 1
console.log(firstTwo, amounts);         // original untouched

const removed = amounts.splice(1, 1);   // remove 1 item at index 1
console.log(removed, amounts);          // original changed

slice(start, end) تعمل تمامًا مثل دالة النصوص التي تحمل الاسم نفسه: تنسخ جزءًا وتترك الأصل دون مساس. أما splice(start, count) فتقصّ عناصر من المصفوفة نفسها وتُرجع ما أزالته. وإن احتجت يومًا إلى إزالة عنصر دون تغيير الأصل، فاستخدم filter بدلًا منها، وستتعرّف عليها حالًا.

map: حوّل كل عنصر

تستدعي map دالة لكل عنصر، وتجمع القيم المُعادة في مصفوفة جديدة بالطول نفسه:

JavaScript
const amounts = [450, 1220, 270];
const formatMoney = (cents) => `${(cents / 100).toFixed(2)}`;

const labels = amounts.map(formatMoney);
console.log(labels);
console.log(amounts);  // unchanged

إنها النسخة المدمجة من دالة applyToAll التي كتبتها في درس الدوال. استخدم map كلما كان السؤال “حوّل كل X إلى Y”: السنتات إلى نصوص بالدولار، أو الدولارات إلى سنتات، أو قيم النموذج الخام إلى قيم نظيفة. يستلم الـ callback كل عنصر بدوره، ويجب أن يُرجع نسخته الجديدة. وكل ما يُرجعه يوضع في الموضع نفسه من النتيجة، لذلك يكون في المخرجات دائمًا عدد العناصر نفسه الذي في المدخلات.

filter: احتفظ ببعض العناصر فقط

تستدعي filter دالة اختبار لكل عنصر، وتُرجع مصفوفة جديدة فيها فقط العناصر التي أرجع لها الاختبار قيمة truthy:

JavaScript
const amounts = [450, -300, 1220, -50, 270];
const spending = amounts.filter((a) => a > 0);
const refunds = amounts.filter((a) => a < 0);
console.log(spending, refunds);

قد تكون النتيجة أقصر من الأصل، بل قد تكون فارغة، لكن filter لا تغيّر العناصر نفسها أبدًا.

find وsome وevery: البحث

أربع دوال تجيب عن أسئلة البحث، وكل منها تتوقّف بمجرّد أن تعرف الإجابة:

JavaScript
const amounts = [450, 12000, 1220, 15000];

console.log(amounts.find((a) => a >= 10000));       // first match: 12000
console.log(amounts.findIndex((a) => a >= 10000));  // its position: 1
console.log(amounts.some((a) => a < 0));            // any refunds? false
console.log(amounts.every((a) => a > 0));           // all positive? true

find تُرجع العنصر نفسه، أو undefined إن لم يطابق شيء. وfindIndex تُرجع موضعه، أو -1. أما some وevery فتُرجعان قيمًا منطقية: “واحد على الأقل” و"جميعها".

السلسلة: قراءة خطّ معالجة

لأن filter وmap كلتيهما تُرجعان مصفوفات، يمكنك استدعاء إحداهما على ناتج الأخرى:

JavaScript
const amounts = [450, -300, 12000, 1220];
const bigOnes = amounts
  .filter((a) => a >= 1000)
  .map((a) => `${(a / 100).toFixed(2)}`);
console.log(bigOnes);

اقرأها من الأعلى إلى الأسفل كوصفة طبخ: ابدأ بكل المبالغ، واحتفظ بما قيمته 10 دولارات أو أكثر، ثم نسّقها. كل خطوة تستلم مخرجات الخطوة التي قبلها. ووضع كل خطوة في سطر خاص بها يبدأ بالنقطة يُبقي السلاسل الطويلة مقروءة.

حتى الآن تحمل المصفوفات أرقامًا بسيطة. لكن المصاريف الحقيقية لها تسمية ومبلغ وفئة معًا، وهذا يستدعي الكائنات، موضوع الدرس التالي.

أهم النقاط

  • const تمنع الاسم من الإشارة إلى مصفوفة أخرى، لكن محتويات المصفوفة نفسها يمكن أن تتغيّر عبر push أو pop أو الإسناد بالفهرس.
  • بعض الدوال تعدّل المصفوفة نفسها (push وpop وsplice وsort)؛ وبعضها يُرجع قيمة جديدة ويتركها دون مساس (slice وmap وfilter).
  • map تُرجع مصفوفة جديدة بالطول نفسه وقد حُوّل كل عنصر فيها؛ وfilter تُرجع مصفوفة جديدة فيها العناصر التي تجتاز الاختبار فقط.
  • find تُرجع أول عنصر مطابق أو undefined؛ وsome وevery تجيبان عن أسئلة نعم/لا حول القائمة كلها.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

ماذا يطبع هذا الكود؟

JavaScript
const amounts = [450, 1220];
amounts.push(270);
console.log(amounts.length);
السؤال 2 من 4

const amounts = [450, -300, 1220]; ماذا تُرجع amounts.find((a) => a > 5000)؟

السؤال 3 من 4

ماذا يطبع هذا الكود؟

JavaScript
const out = [450, 1220].map((c) => {
  c / 100;
});
console.log(out);
السؤال 4 من 4

تحتاج إلى معرفة إن كان لكل مصروف في القائمة فئة. أيّ دالة تناسب؟

تدريب

بيئة تجريب الكود

اطرح أسئلة على قائمة

أكمل أربع دوال باستخدام دوال المصفوفات بدلًا من حلقات for. ولا يجوز لأيّ منها أن تغيّر المصفوفة التي تستلمها.

  • spendingOnly(amounts) تُرجع مصفوفة جديدة فيها المبالغ الموجبة فقط.
  • formatAll(amounts) تُرجع كل المبالغ منسّقة بالدالة المعطاة formatMoney.
  • firstBig(amounts) تُرجع أول مبلغ قيمته 10000 سنت أو أكثر، أو undefined.
  • hasRefund(amounts) تُرجع true إن كان أي مبلغ سالبًا.
const amounts = [450, -300, 12000, 1220, -50, 15000];
const formatMoney = (cents) => `${(cents / 100).toFixed(2)}`;

// Use array methods, not for loops, and never change the array you receive.

// Only the positive amounts
function spendingOnly(amounts) {
  return amounts;
}

// Every amount formatted with formatMoney
function formatAll(amounts) {
}

// The first amount of 10000 cents or more, or undefined
function firstBig(amounts) {
}

// true if any amount is negative
function hasRefund(amounts) {
  return false;
}

console.log(spendingOnly(amounts), formatAll(amounts));
جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

الـ console

(لا مخرجات)

الفحوص

  • spendingOnly تُرجع المبالغ الموجبة فقط
  • formatAll تنسّق كل المبالغ
  • firstBig تُرجع أول مبلغ قيمته 10000 أو أكثر، أو undefined
  • hasRefund تكتشف المبالغ السالبة
  • لا تغيّر أيّ من الدوال مصفوفة المدخلات