الوحدة 3 · العمل مع البيانات
المصفوفات ودوالها اليومية
18 دقراءة 4 دبيئة تجريب الكود
الدرس في لمحة
تُنشئ المصفوفات وتغيّرها، وتعرف أيّ دوالها تعدّلها في مكانها، وتستبدل الحلقات المكتوبة يدويًا بـ map وfilter وfind وsome وevery لتحويل القوائم والبحث فيها.
- 1إنشاء المصفوفات وتغييرها
- 2map: حوّل كل عنصر
- 3filter: احتفظ ببعض العناصر فقط
- 4find وsome وevery: البحث
- 5السلسلة: قراءة خطّ معالجة
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
مصاريف Pocket قائمة، ومعظم ميزاته أسئلة حول هذه القائمة. أيّها مبالغ مستردّة؟ كيف تبدو منسّقة بالدولار؟ هل يوجد ما يتجاوز 100 دولار؟ يمكنك الإجابة عن كل هذا بحلقات القسم 2، لكن المصفوفات تأتي مع دوال (methods) تجيب عن كل سؤال منها بسطر واحد مقروء.
إنشاء المصفوفات وتغييرها
المصفوفة قائمة مرتّبة من القيم. وقد رأيت أساسياتها: أقواس مربّعة، ومواضع تبدأ من 0، وlength.
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 = [...]، أي توجيه الاسم إلى مصفوفة مختلفة، هي التي ترمي خطأ.
هذه الفكرة، أن الاسم يشير إلى قائمة وأن عدّة عمليات تغيّر القائمة نفسها، هي سبب ضرورة معرفة أيّ الدوال تعدّل في المكان (mutate):
| تغيّر المصفوفة نفسها | تُرجع شيئًا جديدًا وتترك المصفوفة كما هي |
|---|---|
push، pop، shift، unshift |
slice، concat |
splice |
map، filter |
sort، reverse |
toSorted، toReversed |
التعديل في المكان ليس خطأً، لكنه يفاجئ الناس حين يتشارك جزءان من البرنامج مصفوفة واحدة. والدوال في باقي هذا الدرس لا تعدّل في المكان أبدًا، وهذا جزء كبير من سبب شعبيتها.
والثنائي الذي يُربك الجميع هو slice وsplice، يفصل بينهما حرف واحد وسلوكهما متعاكس:
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 changedslice(start, end) تعمل تمامًا مثل دالة النصوص التي تحمل الاسم نفسه: تنسخ جزءًا وتترك الأصل دون مساس. أما splice(start, count) فتقصّ عناصر من المصفوفة نفسها وتُرجع ما أزالته. وإن احتجت يومًا إلى إزالة عنصر دون تغيير الأصل، فاستخدم filter بدلًا منها، وستتعرّف عليها حالًا.
map: حوّل كل عنصر
تستدعي map دالة لكل عنصر، وتجمع القيم المُعادة في مصفوفة جديدة بالطول نفسه:
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:
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: البحث
أربع دوال تجيب عن أسئلة البحث، وكل منها تتوقّف بمجرّد أن تعرف الإجابة:
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? truefind تُرجع العنصر نفسه، أو undefined إن لم يطابق شيء. وfindIndex تُرجع موضعه، أو -1. أما some وevery فتُرجعان قيمًا منطقية: “واحد على الأقل” و"جميعها".
السلسلة: قراءة خطّ معالجة
لأن filter وmap كلتيهما تُرجعان مصفوفات، يمكنك استدعاء إحداهما على ناتج الأخرى:
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تجيبان عن أسئلة نعم/لا حول القائمة كلها.