اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'collections'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 2 نتائج

  1. أشرنا في المقال السابق إلى عدة أنواع أساسية من البيانات البرمجية، وفي هذا المقال سنتابع مع ذكر نوع هام جدًا منها، وهو البيانات التجميعية collections. التجميعات Collections لقد شكلت علوم الحاسوب نظامًا كاملًا يدرس التجميعات وسلوكياتها المختلفة، والتي تسمى بالحاويات أحيانًا أو التسلسلات، وسننظر أولًا في التجميعات المدعومة في بايثون وجافاسكربت وVBScript، ثم نختم بملخص موجز لبعض أنواع التجميعات الأخرى التي قد نراها في لغات أخرى. القوائم Lists لا شك في أن القوائم غنية عن تعريفها، إذ نستخدمها في حياتنا كل يوم، فهي مجرد سلسلة من العناصر، حيث نستطيع إضافة عناصر إلى تلك السلسلة أو حذفها، وعادةً ما نضيف العناصر في نهاية القائمة إذا كانت مكتوبةً على الورق، فلا نستطيع إدخال عنصر في المنتصف مثلًا، على عكس القوائم الإلكترونية في برنامج معالج النصوص مثلًا، إذ نستطيع وضع العنصر الجديد في أي مكان، كما نستطيع البحث في القائمة لنتحقق من وجود شيء بعينه فيها، لكن يجب التنقل في القائمة من أعلاها لأسفلها عنصرًا عنصرًا متحققين من كل عنصر لنرى إذا كان ما نريده موجودًا أم لا، وتُعَد القوائم أحد أنواع التجميعات التي لا غنى عنها في أغلب لغات البرمجة الحديثة. لا تحتوي جافاسكربت ولا VBScript على قوائم مضمّنة فيها، لكن نستطيع محاكاة مزايا القوائم بواسطة المصفوفات في جافاسكربت، وبتجميعات بيانات أخرى في VBScript كما سنشرح لاحقًا؛ أما لغة بايثون فالقوائم مضمّنة تلقائيًا فيها، وتستطيع تنفيذ العمليات الأساسية التي تحدثنا عنها جميعًا، إضافةً إلى القدرة على فهرسة العناصر فيها، والفهرسة هي الإشارة إلى عنصر في القائمة برقم تسلسله، على فرض أن العنصر الأول يبدأ من الصفر، كما توفر بايثون عدة عمليات أخرى على التجميعات، وتنطبق كلها تقريبًا على القوائم، كما تنطبق مجموعة فرعية منها على أنواع أخرى من التجميعات بما في ذلك السلاسل النصية التي هي مجرد نوع خاص من القوائم، إذ هي قوائم من المحارف. تُستخدم الأقواس المربعة لإنشاء القوائم والوصول إليها في بايثون، ويمكن إنشاء قائمة فارغة باستخدام زوج من تلك الأقواس ليس بينهما شيء، أو إنشاء قائمة تحتوي على قيم تفصل بينها فاصلة إنجليزية ",": >>> aList = [] >>> another = [1,2,3] >>> print( another ) [1, 2, 3] نستطيع الوصول إلى العناصر المنفردة باستخدام رقم الفهرس، حيث يحمل العنصر الأول رقم 0 داخل الأقواس المربعة، فإذا أردنا الوصول إلى العنصر الثالث مثلًا -والذي سيحمل الترتيب 2 داخل الأقواس بما أننا نبدأ من الصفر-، فإننا نصل إليه كما يلي: >>> print( another[2] ) 3 كما نستطيع تغيير قيم العناصر في القائمة بطريقة مماثلة: >>> another[2] = 7 >>> print( another ) [1, 2, 7] لاحظ كيف تغير العنصر الثالث -الذي فهرسه 2- من 3 إلى 7. يمكن استخدام أرقام الفهرس السالبة للوصول إلى عناصر القائمة من نهايتها، فإذا أردنا العنصر الأخير من القائمة نستخدم ‎-1: >>> print( another[-1] ) 7 وتضاف العناصر الجديدة ملحقةً إلى نهاية القائمة باستخدام العملية append()‎: >>> aList.append(42) >>> print( aList ) [42] كما يمكن الاحتفاظ بقائمة داخل قائمة أخرى، فإذا ألحقنا القائمة الثانية بالقائمة الأولى فستكون على النحو التالي: >>> aList.append(another) >>> print( aList ) [42, [1, 2, 7]] لاحظ كيف تكون النتيجة قائمةً من عنصرين، لكن العنصر الثاني عبارة عن قائمة بحد ذاته كما نرى من القوسين المحيطين به، ونستطيع الآن الوصول إلى العنصر 7 باستخدام فهرس مزدوج: >>> print( aList[1][2] ) 7 حيث يستخرج الفهرس 1 -الفهرس الأول- العنصر الثاني من القائمة الأولى -الذي هو نفسه القائمة الثانية-، ثم يستخرج الفهرس 2 -الفهرس الثاني- العنصر الثالث في القائمة الثانية، وهو العنصر 7. هذا التداخل وإن بدا معقدًا إلا أنه مفيد للغاية، حيث يسمح ببناء جداول من البيانات بفعالية، كما يلي: >>> row1 = [1,2,3] >>> row2 = ['a','b','c'] >>> table = [row1, row2] >>> print( table ) [ [1,2,3], ['a','b','c'] ] >>> element2 = table[0][1] >>> print( element2 ) 2 يمكن استخدام هذه الطريقة في إنشاء دفتر عناوين يكون كل مدخل فيه عبارةً عن قائمة من الاسم والعنوان، ويوضح المثال التالي دفتر عناوين فيه مدخلان: >>> addressBook = [ ... ['Samy', '9 Some St',' Anytown', '0123456789'], ... ['Warda', '11 Nother St', 'SomePlace', '0987654321'] ... ] >>> لاحظ كيف أنه رغم إدخالنا لأربعة أسطر من النص، إلا أن بايثون تعاملت معها على أنها سطر واحد، كما نستنتج من محثات ...، وهذا لأن بايثون ترى أن عدد الأقواس الافتتاحية لا يطابق عدد أقواس الإغلاق، ولهذا تستمر في قراءة المدخلات إلى أن يحدث ذلك التطابق. قد تكون هذه طريقةً فعالةً للغاية في بناء هياكل بيانات معقدة بسرعة مع جعل الهيكل العام -أي قائمة القوائم- واضحًا للقارئ. تدريب استخرج رقم هاتف سامي -العنصر 3 من الصف الأول-، وتذكر أن الفهارس تبدأ من الصفر وليس الواحد، ثم أضف بعض المدخلات من عندك باستخدام عملية append()‎. لاحظ أننا نفقد بياناتنا عند الخروج من بايثون، لكن سنرى كيفية الاحتفاظ بها عند شرح الملفات في هذا الكتاب. يُستخدم الأمر del لحذف العناصر من القائمة: >>> del aList[1] >>> print( aList ) [42] لاحظ أن del لا يحتاج إلى أقواس حول القيمة، على عكس دالة الطباعة، لأنه أمر وليس دالةً، قد يكون هذا الفرق طفيفًا، لكن إذا وضعت أقواسًا حول القيمة فلا بأس إذ ستظل صالحةً. إذا أردنا ضم قائمتين معًا لجعلهما قائمةً واحدةً، فسنتخدم عامل الضم + الذي رأيناه في ضم السلاسل النصية من قبل: >>> newList = aList + another >>> print( newList ) [42, 1, 2, 7] لاحظ اختلاف هذا المثال عن سابقه الذي ألحقنا فيها القائمتين معًا، فقد كان لدينا عنصران، وكان العنصر الثاني منهما عبارةً عن قائمة؛ أما هنا فلدينا أربعة عناصر لأن عناصر القائمة الثانية أضيفت عنصرًا عنصرًا إلى newList، فإذا أردنا الوصول إلى العنصر 1 هذه المرة فسنحصل على 1، وهو العنصر الثاني هنا، بدلًا من الحصول على قائمة فرعية. >>> print( newList[1] ) 1 يمكن تطبيق إشارة عملية الضرب هنا مثل عامل تكرار لملء القائمة بمضاعفات نفس القيمة: >>> zeroList = [0] * 5 >>> print( zeroList ) [0, 0, 0, 0, 0] كما يمكن إيجاد فهرس عنصر ما في قائمة باستخدام العملية index()‎ كما يلي: >>> print( [1,3,5,7].index(5) ) 2 >>> print( [1,3,5,7].index(9) ) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: list.index(x): x not in list لاحظ أن محاولة العثور على فهرس لشيء غير موجود في القائمة ينتج خطأً، فإذا أردنا التحقق من وجود شيء في تجميعة ما فسنستخدم العامل in كما يلي: >>> print( 5 in [1,3,5,7] ) True >>> print( 9 in [1,3,5,7] ) False النتائج قيم بوليانية كما نرى، إما True أو False، وسنرى كيف نستخدم تلك النتائج في فصل لاحق. أخيرًا، يمكن معرفة طول القائمة باستخدام الدالة المضمَّنة len()‎: >>> print( len(aList) ) 1 >>> print( len(newList) ) 4 >>> print( len(zeroList) ) 5 لا تدعم جافاسكربت ولا VBScript نوع القائمة مباشرةً، وسنرى أن لديهما نوع بيانات يحاكي وظيفة القائمة هنا، وهو نوع المصفوفات arrays. الصف Tuple توفر بعض لغات البرمجة بنية بيانات اسمها صف tuple، وما هي إلا تجميعة عشوائية من القيم لكنها تعامَل مثل وحدة واحدة، وهي تشبه القوائم في نواحٍ كثيرة، لكن مع فرق أن وحدات tuple غير قابلة للتغيير، مما يعني أننا لا نستطيع التعديل عليها أو إلحاق شيء إليها بعد إنشائها أول مرة، تمثَّل وحدات tuple في بايثون بقائمة من القيم المفصول بينها بفواصل: >>> aTuple = 1,3,5 >>> print( aTuple[1] ) # استخدم الفهرسة مثل القوائم 3 >> aTuple[2] = 7 # tuple خطأ، لا يمكن تعديل Traceback (innermost last): File "<pyshell#20>", line 1, in ? aTuple[2] = 7 TypeError: object doesn't support item assignment من المعتاد إحاطة التسلسل بأقواس، وهذا يضمن عدم إخراج القيم من السياق، إضافةً إلى التذكير المرئي بأنها وحدة واحدة، أي وحدة صف، توضح البيانات التالية هذا الأمر: >>> t1 = 1,2,3 * 3 >>> t2 = (1,2,3) * 3 >>> print(t1) (1, 2, 9) >>> print( t2 ) (1, 2, 3, 1, 2, 3, 1, 2, 3) يطبَّق الضرب في الحالة الأولى على العنصر الأخير فقط في وحدة Tuple، أما في الحالة الثانية فتضاعَف المجموعة بالكامل، ولتجنب مثل هذا الغموض، سنستخدم الأقواس في كل مرة نصف فيها وحدات tuple. لعل أحد أهم الأمور التي يجب تذكرها هو أن الأقواس المربعة تستخدم لفهرسة وحدات tuple، بينما تُستخدم الأقواس العادية لتعريفها، ولا يمكن تغييرها بعد إنشائها؛ أما في سوى هذه الأمور فما ينطبق من العمليات على القوائم ينطبق أيضًا على الصفوف، ورغم أننا لا نستطيع تعديل وحدة الصف بعد إنشائها؛ إلا أننا نستطيع إضافة أعضاء جديدة إليها باستخدام عامل الإضافة، فهذا سينشئ صفًا جديدًا، كما يلي: >>> tup1 = (1,2,3) >>> tup2 = tup1 + (4,) # وليس عددًا صحيحًا tuple الفاصلة تجعل هذا وحدة >>> print( tup2 ) (1,2,3,4) إذا لم نستخدم الفاصلة اللاحقة بعد العدد 4 فستفسره بايثون على أنه العدد الصحيح 4 داخل أقواس وليس على أنه صف، لكن سنحصل على خطأ بأي حال بما أنه لا يمكن إضافة أعداد صحيحة إلى الصفوف، لذا سنضيف الفاصلة لنخبر بايثون بأن تعامل الأقواس على أنها صف، فبإضافة الفاصلة الزائدة نقنع بايثون بأن وحدة الصف التي فيها مدخل واحد هي صف فعلًا؛ أما بالنسبة للغتي جافاسكربت وVBScript فليس لديهما أي مفهوم للصف tuple. القاموس Dictionary أو Hash يحتوي هذا النوع من البيانات على قيمة ترتبط بمفتاح، كما يربط القاموس اللغوي المعنى بكلمة، وقد تكون تلك القيمة سلسلةً نصيةً أو لا، وتُجلب تلك القيمة بفهرسة القاموس بالمفتاح، ولا يلزم أن يكون المفتاح سلسلةً من الأحرف -رغم أنه غالبًا كذلك-، وهذا على خلاف القاموس اللغوي، فقد يكون أي نوع غير قابل للتغير، بما في ذلك الأعداد والصفوف، كما قد تحتوي القيم المرتبطة بالمفاتيح على أي نوع من البيانات. تُستخدم القواميس داخليًا بواسطة تقنية برمجة متقدمة تسمى بجداول Hash، لهذا يشار أحيانًا إلى القاموس باسم Hash، ونظرًا لأننا نتوصَّل إلى قيم القاموس عبر المفتاح، فلا توضع إلا عناصر ذات مفاتيح فريدة، رغم أن المفتاح قد يشير إلى قائمة من القيم. تُعَد القواميس مفيدةً للغاية في هيئة هياكل للبيانات، ونجدها في بايثون مثل نوع بيانات مضمَّن في اللغة نفسها، لكن قد نحتاج في بعض اللغات الأخرى إلى استخدام وحدة module، أو بناء واحدة بأنفسنا إذا أردنا استخدام القواميس، وتُستخدم القواميس بعدة طرق سنراها كثيرًا في الأمثلة التالية من الكتاب؛ أما الآن فسنرى أولًا كيف ننشئ قاموسًا في بايثون، ثم نملؤه ببعض المدخلات ونقرؤها. >>> dct = {} >>> dct['boolean'] = "A value which is either true or false" >>> dct['integer'] = "A whole number" >>> print( dct['boolean'] ) A value which is either true or false لاحظ أننا نبدأ القاموس بأقواس معقوصة ثم نستخدم الأقواس المربعة لإسناد القيم وقراءتها، كما تُستخدم دالة النوع dict()‎ لإعادة قاموس فارغ، ويمكن بدء القاموس أثناء إنشائه كما فعلنا في حالة القوائم، باستخدام الصيغة التالية: >>> addressBook = { ... 'Samy' : ['Samy', '9 Some St',' Anytown', '0123456789'], ... 'Warda' : ['Warda', '11 Nother St', 'SomePlace', '0987654321'] ... } >>> يُفصل المفتاح والقيمة بنقطتين رأسيتين، وتُفصل الأزواج بفواصل إنجليزية ,، كما يمكن تخصيص القاموس باستخدام صيغة مختلفة -انظر أدناه-، ويمكنك اختيار إحدى الطريقتين لاستخدامها. >>> book = dict(Samy=['Samy', '9 Some St',' Anytown', '0123456789'], ... Warda=['Warda', '11 Nother St', 'SomePlace', '0987654321']) >>> print( book['Samy'][3] ) 0123456789 لاحظ أننا لا نحتاج إلى علامات اقتباس حول المفتاح في التعريف، لأن بايثون تفترض أنها سلسلة نصية، لكننا نحتاج إليها بأي حال من أجل استخراج القيم، وهذا الأسلوب يَحُد من عملية الطريقة الثانية، لهذا يفضِّل المبرمجون استخدام الطريقة الأولى التي يستخدمون الأقواس المعقوصة فيها، وبهذا نكون قد أنشأنا دفتر عناوين من قاموس مفاتيحه هي الأسماء، ويخزن قوائمنا مثل قيم، ونريد أن نستخدم الاسم وحده لجلب كل المعلومات بدلًا من حساب الفهرس العددي، كما يلي: >>> print( addressBook['Warda'] ) ['Warda', '11 Nother St', 'SomePlace', '0987654321'] >>> print( addressBook['Samy'][3] ) 0123456789 في الحالة الثانية فهرسنا القيم المعادة من أجل الحصول على رقم هاتف فقط، لكن يمكن جعل هذا أسهل عبر إنشاء بعض المتغيرات وإسناد قيم الفهرسة المناسبة، كما يلي: >>> name = 0 >>> street = 1 >>> town = 2 >>> tel = 3 نستطيع الآن استخدام تلك المتغيرات لنعرف اسم مدينة Warda: >>> print( addressBook['Warda'][town] ) SomePlace لاحظ أن town ليست داخل علامات اقتباس لأنها اسم متغير، وستحوله بايثون إلى قيمة الفهرس التي أسندناها -وهي 2-، على عكس 'Warda' المحاطة بعلامات اقتباس لأن المفتاح هو سلسلة نصية، وهنا بدأ دفتر العناوين يتحول إلى ما يشبه التطبيق القابل للاستخدام بسبب مزايا القواميس، حيث لن نحتاج إلى كثير من العمل الإضافي لحفظ البيانات واستعادتها وإضافة محث استعلام ليسمح لنا بتحديد البيانات التي نريدها. يمكن استخدام قاموس لتخزين البيانات، وسيكون دفتر العناوين حينها قاموسًا مفاتيحه الأسماء، وستكون القيم قواميس مفاتيحها هي حقول الأسماء، كما يلي: addressBook = { ... 'Samy' : {'name': 'Samy', ... 'street': '9 Some St', ... 'town': 'Anytown', ... 'tel': '0123456789'}, ... 'Warda' : {'name': 'Warda', ... 'street': '11 Nother St', ... 'town': 'SomePlace', ... 'tel': '0987654321'} ... } لاحظ أن هذا التنسيق سهل القراءة رغم أنه يحتاج إلى الكثير من الكتابة، ويُشار إلى مثل هذه البيانات التي تخزَّن بتنسيق يجمع بين المعنى والمحتوى في تنسيق مقروء للبشر باسم البيانات الموثقة ذاتيًا. حين ندخِل هيكل بيانات داخل هيكل آخر مطابق له، كما في حالة إدخال قاموس داخل قاموس آخر هنا، فإنه يُسمى بالتداخل أو التشعب nesting، حيث يكون القاموس الداخلي قاموسًا متشعبًا من الخارجي، ويمكن الوصول إلى تلك البيانات بطريقة تشبه أسلوب القوائم ذات الفهارس المسماة: >>> print( addressBook['Warda']['town'] ) SomePlace وليس ثمة اختلاف إلا في الاقتباس الإضافي حول كلمة town التي هي ليست موجودةً في حالة القوائم، وميزة هذا الأسلوب أننا نستطيع إدخال حقول جديدة دون تعطيل الشيفرة الحالية، بينما نضطر في حالة الفهارس المسماة إلى العودة لتغيير جميع قيم الفهارس، وتبرز أهمية هذا الأسلوب عند استخدام نفس البيانات في عدة برامج، فيكون بذل قليل من الجهد هنا موفرًا لكثير من العمل لاحقًا في المستقبل. لا تدعم القواميس كثيرًا من عوامل التجميعات التي رأيناها من قبل، فلا تدعم عامل الضم ولا التكرار ولا الإلحاق، رغم إمكانية إسناد أزواج من المفاتيح والقيم مباشرةً كما رأينا في بداية هذا القسم. وتُستخدم العملية keys()‎ لتسهيل الوصول إلى مفاتيح القاموس، إذ تسمح لنا بالحصول على قائمة من جميع المفاتيح الموجودة في قاموس ما، فإذا أردنا الحصول على كل الأسماء الموجودة في دفتر العناوين الذي أنشأناه من قبل؛ فسيكون ذلك كما يلي: >>> print( list(addressBook.keys()) ) ['Samy','Warda'] لاحظ أننا استخدمنا list()‎ للحصول على قيم المفاتيح الحقيقية، أما إذا حذفنا list()‎ فسنحصل على نتيجة غريبة بعض الشيء لن نذكرها الآن. لاحظ أن القواميس لا تخزِّن مفاتيحها بنفس ترتيب إدخالها، فقد نرى أن المفاتيح تظهر بترتيب غريب عن ترتيبها الأول، بل قد يتغير الترتيب بتغير الوقت، لكن هذا لا يهم بما أننا نستطيع استخدام المفاتيح للوصول إلى البيانات المرتبطة بها، إذ سنحصل على القيم الصحيحة في كل مرة. يمكن الحصول على قائمة بجميع القيم أيضًا باستخدام العملية values()‎، فجرب ذلك على دفتر العناوين وانظر إن استطعت تنفيذها بنجاح، استخدم مثال keys()‎ السابق للاسترشاد به، ويمكن بصورة مماثلة استخدام العامل in على القاموس ليخبرنا هل توجد قيمة ما في صورة مفتاح للقاموس أم لا. قواميس VBScript توفر VBScript كائن قاموس يقدم تسهيلات تماثل قاموس بايثون، لكن استخدامه مختلف قليلًا عن حالة بايثون، حيث ننشئ القاموس هنا بالتصريح عن متغير يحمل الكائن، ثم ننشئ ذلك الكائن، وبعد ذلك نضيف المدخلات إلى القاموس الجديد كما يلي: Dim dict ' Create a variable. Set dict = CreateObject("Scripting.Dictionary") dict.Add "a", "Athens" ' Add some keys and items. dict.Add "b", "Belgrade" dict.Add "c", "Cairo" لاحظ أن دالة CreateObject توضح أننا ننشئ كائن "Scripting.Dictionary"، وهو كائن Dictionary من وحدة Scripting الخاصة بلغة VBScript، وسندرس ذلك بالتفصيل حين نأتي على الكائنات، لكننا نأمل بذكرها هنا أن تتذكر مفهوم استخدام كائن من وحدة ما كما ذكرنا في فصل التسلسلات البسيطة. يجب كذلك استخدام كلمة Set المفتاحية عند إسناد كائن إلى متغير في VBScript. نستطيع الآن أن نصل إلى البيانات كما يلي: item = dict.Item("c") ' Get the item. dict.Item("c") = "Casablanca" ' Change the item توجد عمليات أخرى لإزالة عنصر ما، والتحقق من وجود مفتاح ما، والحصول على قائمة بجميع المفاتيح وغير ذلك. كما يمكن تخزين أي نوع من الكائنات في القاموس إذ أن الأمر ليس مقصورًا على السلاسل النصية فقط، وهذا يعني أننا نستطيع إنشاء قواميس متشعبة بما أن القاموس نفسه ما هو إلا كائن. فيما يلي نسخة كاملة مبسطة عن مثال دفتر العناوين السابق، ولكن في VBScript: <script type="text/VBScript"> Dim addressBook Set addressBook = CreateObject("Scripting.Dictionary") addressBook.Add "Samy", "Samy, 9 Some St, Anytown, 0123456789" addressBook.Add "Warda", "Warda, 11 Nother St, SomePlace, 0987654321" MsgBox addressBook.Item("Warda") </script> ما فعلناه هنا هو تخزين جميع البيانات مثل سلسلة نصية واحدة بدلًا من استخدام قائمة -وهذا يصعّب استخراج الحقول المنفردة على عكس القائمة والقاموس-، ثم نستطيع الوصول إلى تفاصيل Warda ونطبعها في صندوق رسالة. قواميس جافاسكربت لا تحتوي جافاسكربت على كائن قاموس فيها، رغم أننا نستطيع الوصول إلى كائن Scripting.Dictionary الذي ذكرناه في VBScript باستخدام إنترنت إكسبلورر، لنتمكن حينئذ من استغلال جميع مزاياه، لكن لن نشرح ذلك هنا مرةً أخرى بما أنه نفس الكائن. تستطيع هنا الانتقال إلى الفصل التالي إذا كنت قد شعرت بالملل من طول هذا الفصل وتريد كتابة شيفرات وتجربتها، لكن تذكر أن تعود وتنهي هذا الفصل حين تتعرض لأنواع بيانات لم نذكرها حتى الآن. المصفوفات أو المتجهات Arrays المصفوفة هي أحد أنواع التجميعات التي يمتد تاريخها موازيًا لتاريخ الحوسبة، وما هي إلا مجموعة من العناصر المفهرسة بحيث يسهل جلبها بسرعة، والغالب أننا يجب أن نحدد عدد العناصر التي نريد تخزينها في المصفوفة مسبقًا، إذ يُخزَّن نوع واحد أيضًا من البيانات في المصفوفة عادةً، وهاتان الخاصيتان للمصفوفة هما اللتان تميزانها عن القوائم مثلًا. لاحظ أننا قد قلنا "عادة"، و"الغالب" للدلالة على غالب الحالات، ذلك أن لغات البرمجة المختلفة لها أساليبها الخاصة التي تصف المصفوفات، لذا يصعب وضع وصف يغطي جميع الحالات. تدعم لغة بايثون المصفوفات من خلال وحدة تسمى array، لكننا لا نحتاج إليها إلا نادرًا مع استثناء حالات الحساب الرياضي عالي الأداء؛ أما غير ذلك فنستخدم القوائم، وفي لغتي جافاسكربت وVBScript؛ تتكون المصفوفات مثل نوع بيانات، لذا سننظر في كيفية استخدامها فيهما. مصفوفات VBScript تكون المصفوفة في VBScript تجميعة بيانات ذات طول ثابت، حيث يمكن التوصَل إليها بواسطة فهرس عددي، ويصرَّح عنها ويوصَل إليها كما يلي: Dim AnArray(42) ' A 43! element array AnArray(0) = 27 ' index starts at 0 AnArray(1) = 49 AnArray(42) = 66 ' assign to last element myVariable = AnArray(1) ' read the value تُستخدم الكلمة المفتاحية Dim لتحديد أبعاد المتغير، وهي الطريقة التي نخبر بها VBScript عن المتغير، فهي تتوقع إذا بدأنا الشيفرة بـ OPTION EXPLICIT؛ أن نحدد أبعاد أي متغير نستخدمه من خلال Dim، وهذا سلوك محمود يؤدي إلى برامج ثابتة قوية الأداء، كما أننا حددنا آخر فهرس صالح 42، وهذا يعني أن المصفوفة فيها 43 عنصرًا بما أنها تبدأ من الصفر. تُستخدم الأقواس في VBScript لحساب أبعاد المصفوفة وفهرستها، وهذا على خلاف الأقواس المربعة المستخدمة في بايثون وجافاسكربت، حيث لا يوجد في VBScript نوع بيانات إلا النوع Variant، والذي يخزِّن أي نوع من أنواع القيم، وبناءً عليه فلا تخزِّن المصفوفات في هذه اللغة إلا المتغيرات بما أن عناصر المصفوفة تكون من نوع واحد، لكن في نفس الوقت، فإن المتغير هنا قد يحمل أي نوع من أنواع القيم، مما يعني مرةً أخرى أن المصفوفات في VBScript تخزن أي شيء، وهذا أمر مربك عند التفكير فيه. نستطيع أيضًا الإسناد إلى أي موضع في المصفوفة بما في ذلك الموضع الأخير، من غير أن نملأ المواضع التي قبله، وسيُضبط أي عنصر غير مهيأ إلى القيمة الخاصة Null. كما يمكن التصريح عن عدة مصفوفات متعددة الأبعاد لنمذجة جداول بيانات كما في قوائم بايثون إذا استخدمنا تدريب دفتر العناوين مثلًا. Dim MyTable(2,3) ' 3 rows, 4 columns MyTable(0,0) = "Samy" ' Populate Samy's entry MyTable(0,1) = "9 Some Street" MyTable(0,2) = "Anytown" MyTable(0,3) = "0123456789" MyTable(1,0) = "Warda" ' And now Warda... ...and so on... لكن لا توجد طريقة سهلة لملء البيانات دفعةً واحدةً كما فعلنا في قوائم بايثون، بل يجب أن نملأ كل حقل على حدة، وعلى الرغم من وجود الدالة Arrayفي VBScript والتي تستطيع ملء مصفوفة دفعةً واحدةً -مما يعني الحاجة إلى جعلها متشعبةً-؛ فستصبح معقدةً وصعبة القراءة. إذا دمجنا قواميس VBScript مع هذه الخاصية لمصفوفاتها، فسنحصل على نفس النتيجة التي حصلنا عليها في بايثون، كما يلي: <script type="text/VBScript"> Dim addressBook Set addressBook = CreateObject("Scripting.Dictionary") Dim Samy(3) Samy(0) = "Samy" Samy(1) = "9 Some St" Samy(2) = "Anytown" Samy(3) = "0123456789" addressBook.Add "Samy", Samy MsgBox addressBook.Item("Samy")(3) ' Print the Phone Number </script> وآخر ملاحظة متعلق بمصفوفات VBScript هو أنها لا تحتاج إلى أن تكون ثابتة الحجم، على أن هذا لا يعني استمرار إضافة العناصر إليها كما في القوائم، بل يجب أن نغير حجم المصفوفة صراحةً، بالتصريح عن مصفوفة ديناميكية، وذلك بإهمال ذكر الحجم ببساطة، كما يلي: Dim DynArray() ' no size specified ثم إذا أردنا تغيير حجمها نفعل ذلك كما يلي: <script type="text/vbscript"> Dim DynArray() ReDim DynArray(5) ' Initial size = 5 DynArray(0) = 42 DynArray(4) = 26 MsgBox "Before: " & DynArray(4) ' prove that it worked ' Resize to 21 elements keeping the data we already stored ReDim Preserve DynArray(20) DynArray(15) = 73 MsgBox "After Preserve: " & DynArray(4) & " " & DynArray(15)' Old and new still there ' Resize to 51 items but lose all data Redim DynArray(50) MsgBox "Without Preserve: " & DynArray(4) & " Oops, Where did it go?" </script> لاحظ أن السلوك الافتراضي هو حذف كل محتويات المصفوفة عند تغيير حجمها، فإذا أردنا الحفاظ على محتوياتها؛ فيجب أن نخبر VBScript أن تحفظ المحتويات باستخدام Preserve. هذا الأسلوب غير مريح موازنةً بالقوائم التي تغير حجمها تلقائيًا، لكنه يعطي المبرمج تحكمًا أفضل في كيفية تصرف البرنامج، مما يحسن الأمان -ضمن أمور أخرى- نظرًا لأن بعض الفيروسات قد تستغل مخازن البيانات القابلة لتغيير حجمها ديناميكيًا. مصفوفات جافاسكربت رغم أنه يطلَق على هذا النوع من البيانات في جافاسكربت مصفوفة؛ إلا أنها تسمية خاطئة، فهو مزيج غريب يجمع بين مزايا القوائم والقواميس والمصفوفات العادية، حيث نستطيع مثلًا التصريح عن مصفوفة من عشرة عناصر من نوع ما كما يلي: var items = new Array(10); لاحظ استخدام كلمة new المفتاحية، إذ تشبه دالة CreateObject()‎ التي استخدمناها في VBScript لإنشاء قاموس، كذلك فقد استخدمنا الأقواس لتحديد حجم المصفوفة، نستطيع الآن أن نملأ المصفوفة ونصل إليها كما يلي: items[4] = 42; items[7] = 21; var aValue = items[4]; وهنا نستخدم الأقواس المربعة للوصول إلى عناصر المصفوفة، وتبدأ الفهارس فيها من الصفر، لكن رغم هذا كله فمصفوفات جافاسكربت ليست مقيدة بتخزين نوع واحد من البيانات، بل يمكن إسناد أي شيء إلى عناصرها: items[9] = "A short string"; var msg = items[9]; كما يمكن إنشاء مصفوفات من خلال توفير قائمة بالعناصر: var moreItems = new Array("one","two","three",4,5,6); aValue = moreItems[3]; // -> 4 msg = moreItems[0]; // -> "one" كذلك يمكن تحديد طول المصفوفة باستخدام خاصية تدعى length، التي تستخدَم كما يلي: var size = items.length; وعلى غرار أسلوب بايثون في استدعاء الدوال في وحداتها، فإن الصياغة name.property هنا هي نفسها، لكن دون أقواس لاحقة. ورغم أن مصفوفات جافاسكربت تبدأ بالصفر كما ذكرنا؛ إلا أن فهارسها ليست مقصورةً على الأرقام وحدها، ولهذا يمكن استخدام السلاسل النصية أيضًا -والتي تكون هنا أشبه بالقواميس-، كما يمكن توسيع مصفوفة بإسناد قيمة إلى فهرس تكون أكثر من الحد الأقصى الحالي، مما يعني أننا لا نحتاج إلى تحديد الحجم عند إنشاء المصفوفة، رغم كون ذلك سلوكًا مستحسنًا في البرمجة، ويوضح المثال التالي خصائص مصفوفات جافاسكربت التي ذكرناها: <script type="text/javascript"> var items = new Array(10); var moreItems = new Array(1); items[42] = 7; moreItems["foo"] = 42; msg = moreItems["foo"]; document.write("msg = " + msg + " and items[42] = " + items[42] ); </script> إذا شغّلنا تلك الشيفرة في المتصفح فسنرى القيم مكتوبةً على الشاشة. تحقق إن كنت تستطيع متابعة الإسنادات في الشيفرة لتتأكد من سبب ظهور القيم التي تراها على الشاشة. لننظر أخيرًا في مثال دفتر العناوين مرةً أخرى، باستخدام مصفوفات جافاسكربت هذه المرة: <script type="text/javascript"> var addressBook = new Array(); addressBook["Samy"] = new Array("Samy", "9 Some St", "Anytown", "0123456789"); addressBook["Warda"] = new Array("Warda", "11 Nother St", "SomePlace", "0987654321"); document.write(addressBook.Warda); </script> يمكن الوصول إلى المفتاح كما لو كان خاصيةً مثل length، ويتبين مما سبق أن مصفوفات جافاسكربت ما هي إلا هياكل بيانات مرنة للغاية. المكدس Stack يكدِّس العاملون في المطاعم الأطباق النظيفة فوق بعضها، ثم تؤخَذ واحدًا تلو الآخرللعملاء بدءًا من الأعلى، فيكون الطبق الأخير هو آخر طبق يُستخدم، وفي نفس الوقت أقل طبق يُستخدم، يشبه هذا المثال مكدسات البيانات بالضبط، إذ نضع العنصر في قمة المكدس أو نأخذ عنصرًا منه، ويكون العنصر المأخوذ هو آخر عنصر مضاف إلى المكدس، ويطلَق على هذه الخاصية للمكدسات "آخرها دخولًا أولها خروجًا" أو LIFO - Last In First Out. من الخصائص المفيدة في المكدسات أننا نستطيع عكس قائمة العناصر عن طريق دفع القائمة في المكدس ثم إخراجها مرةً أخرى، فتكون النتيجة عكس القائمة الأولى. لا تأتي المكدسات مدمجةً في بايثون ولا VBScript ولا جافاسكربت، بل يجب كتابة بعض التعليمات البرمجية في البرنامج من أجل الحصول على سلوك المكدس، والقوائم هي أفضل نقطة بداية في الغالب للمكدسات بما أنه يمكن زيادة حجمها عند الحاجة. تدريب اكتب مكدسًا باستخدام قائمة في بايثون، وتذكر أننا نلحق العناصر في نهاية القائمة باستخدام append()‎، ونحذفها باستخدام فهارسها عن طريق del()‎، كما تستطيع استخدام ‎-1 مثل فهرس يشير إلى آخر عنصر في القائمة. يجب أن تكون قادرًا على كتابة برنامج -مستفيدًا من هذه الإرشادات- يدفع 4 محارف إلى قائمة ثم يخرجها مرةً أخرى، ثم اطبعها بعد ذلك. راقب الترتيب الذي تستدعي به print و del، وإذا نجحت في هذا، فجرب طباعتها بعكس الترتيب الذي أدخلتها به، استخدم العملية pop()‎ الخاصة بقوائم بايثون والتي تعيد العنصر الأخير وتحذفه في خطوة واحدة، من أجل تسهيل التدريب. الحقيبة Bag في سياق الحديث عن البيانات؛ تُعَد الحقيبة تجميعةً من العناصر التي ليس لها ترتيب محدد، ويمكن أن تحتوي على عناصر مكررة، وتكون لها عادةً عوامل تمكننا من إضافة تلك العناصر وحذفها وإيجادها، فما هي إلا قوائم في اللغات الثلاث التي نستخدمها في الكتاب. المجموعات Sets تخزن المجموعة نوعًا واحدًا من العناصر، ونستطيع التحقق مما إذا كان العنصر موجودًا في المجموعة أم لا -أي عضوًا فيها-، إلى جانب إضافة تلك العناصر وحذفها، بالإضافة إلى دمج مجموعتين معًا بطرق شتى تتوافق مع نظرية المجموعات في الرياضيات، مثل الاتحاد والتقاطع وغيرها، لكن على هذا فالمجموعات لا تكون مرتبةً ولا تهتم للترتيب. لا تستخدم جافاسكربت ولا VBScript المجموعات مباشرةً، لكن يمكن تنفيذ سلوك قريب باستخدام القواميس التي لا تحتوي إلا على مفاتيح بقيم فارغة؛ أما بايثون فتدعم المجموعات مثل نوع من أنواع البيانات فيها، وأحد الاستخدامات البسيطة لها ما يلي: >>> A = set() # أنشئ مجموعة فارغة >>> B = set([1,2,3]) # مجموعة من قائمة >>> C = {3,4,5} # تهيئة مثل [] في القوائم >>> D = {6,7,8} >>> # جرب الآن بعض عمليات القوائم >>> print( B.union(C) ) {1, 2, 3, 4, 5} >>> print( B.intersection(C) ) {3} >>> print( B.issuperset({2}) ) True >>> print( {3}.issubset(C) ) True >>> print( C.intersection(D) == A ) True لاحظ أننا نستطيع تهيئة المجموعة باستخدام الأقواس المعقوصة، لكن هذا قد يتشابه مع القواميس، لذا نفضل استخدام الصيغة set([...]])‎ رغم أنها تتطلب كتابةً أكثر، فيما يلي اختصارات لعمليات الاتحاد والتقاطع: >>> print( B & C ) # B.intersection(C) كما في >>> print( B | C ) # B.union(C) كما في نستطيع أخيرًا التحقق من وجود عنصر ما في مجموعة باستخدام العامل in: >>> print( 2 in B ) True هذه العمليات كافية إلى الآن لما نشرحه، رغم وجود عمليات أخرى على المجموعات. الطابور Queue يشبه الطابور أو قائمة الانتظار؛ المكدس، باستثناء أن العنصر الأول فيه الذي يخرج أولًا أيضًا، ويسمى هذا السلوك "الأول دخولًا الأول خروجًا" أو FIFO - First In First Out، ويُنشَأ الطابور باستخدام قائمة أو مصفوفة. تدريب جرب كتابة طابور باستخدام قائمة، وتذكر أنك تستطيع الإضافة إلى القائمة باستخدام append()‎، وأن تحذف من موضع ما باستخدام del()‎. أضف أربعة محارف إلى الطابور ثم استخرجها واطبعها، غذ يجب أن تُطبع المحارف بنفس الترتيب الذي دخلت به. توجد أنواع أخرى من تجميعات البيانات، لكن ما ذكرناه هنا يغطي أغلب الحالات التي ستراها أثناء البرمجة، بل لن نستخدم إلا قليلًا مما ذكرناه هنا في الشرح، لكن قد ترى بقية الأنواع في المقالات أو مجموعات النقاشات البرمجية. الملفات Files ينبغي أن يكون مستخدم الحاسوب معتادًا على التعامل مع الملفات بما أنها تمثل أساس كل شيء نفعله على الحواسيب، ولا غرو إذًا أن نكتشف أن أغلب لغات البرمجة توفر نوع file خاص من البيانات، لكن أهمية الملفات ومعالجتها ستجعلنا نرجئ الحديث عنها وشرحها إلى فصل لاحق خاص بها. وبهذا تكون قد تعرفت على أبرز البيانات البرمجية التي قد تحتاج إليها. ترجمة -بتصرف- للفصل الخامس: The Raw Materials من كتاب Learning To Program لصاحبه Alan Gauld. اقرأ أيضًا المقال التالي: بعض التسلسلات النصية المهمة لتعلم البرمجة المقال السابق: مدخل إلى البيانات وأنواعها: أنواع البيانات الأساسية مدخل إلى البيانات الوصفية microdata في HTML5 هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت
  2. في الفصل السابق، المجالات، تعلّمنا أنّ Angular تقوم بإنشاء مجالٍ جديد في كلّ مرّةٍ يتمّ فيها استدعاء الباني الخاصّ بالمتحكّم عن طريق ng-controller. هناك حالاتٌ أخرى أيضًا تقوم فيها Angular بإنشاء مجالاتٍ جديدة، وربّما تكون الحالات الأكثر شيوعًا هي عند التّعامل مع مجموعاتٍ من كائناتٍ متشابهة، كما سنرى في هذا الفصل. لا تملك Angular مكوّنًا اسمه Collection على عكس Backbone، ولكنّ دعم Angular الواسع للمجموعات ذات الكائنات المتشابهة يستحقّ إفراد فصلٍ كاملٍ لها، كما سنرى الآن. التهيئةقمنا سابقًا بتضمين المكتبة المستضافة عند Google في رأس مستندات HTML الخاصة بأمثلة الفصول السابقة، وسنضيف في هذا الفصل أسلوب Bootstrap في تنسيق الجداول والقوائم لإعطائها مظهرًا أجمل. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"></script>بعد ذلك نقوم بتحميل وحدة app بتمرير اسمها إلى التّوجيه ng-app. يمكنك اختيار أيّ اسم للوحدة بدلًا من app. <body ng-app="app"> <!-- الأمثلة توضع هنا --> </body>كما شرحنا سابقًا، سنحتاج إلى إضافة بعض الشّيفرات المعيارية لنتجنّب استخدام الوحدات في أمثلة هذا الدرس، وسنغطّي ذلك المفهوم في الفصل القادم في هذا الكتاب. angular.module('app', []); angular.module('app').config(['$controllerProvider', function($controllerProvider) { $controllerProvider.allowGlobals(); }]);نحن جاهزون الآن لنتقدّم في استكشافنا التّفاعليّ للمجموعات (collections) والمرور على العناصر (iteration) ولـAngular. المرور على العناصرفي JavaScript المعتادة، عندما تمرّ على عناصر مجموعة من كائنات متشابهة عن طريق حلقة for، قد تقوم بالتّصريح عن متغيّر محلّيّ ليحتفظ بمرجعٍ للعنصر الحاليّ، على سبيل المثال: var items = [{name: "Item 1"},{name: "Item 2"}]; for (var i = 0; i < items.length; i++) { var item = items[i]; } document.body.innerHTML = item.name;الناتج Item 2ربما تظنّ (تتمنّى؟ تأمل؟ ترجو؟) أن يكون المتغيّر item في المثال السابق موجودًا ضمن مجال أسماءٍ تُنشئه JavaScript عند كل دورةٍ لحلقة for، للأسف، JavaScript لا تقوم بذلك كما يبيّن السطر الأخير في المثال السّابق.فالمتغيّر item متاح للاستخدام خارج الحلقة، لقراءة المزيد عن هذه النقطة، يُرجى مراجعة دليل Mozilla في JavaScript. ولكنّ Angular تتجنّب هذا المأزق عن طريق دعمها الدّاخليّ للمرور على العناصر.وكي نرى كيف تقوم بذلك لنقم أوّلًا بنقل المصفوفة items إلى داخل متحكّم. function ItemsController($scope) { $scope.items = [ {name: 'Item 1', price: 1.99}, {name: 'Item 2', price: 2.99} ]; }تخيّل لو أنّ items مجموعةٌ غير معروفة الطّول، ونحتاج إلى المرور على جميع عناصرها، وإظهار قيمة العنصر name لكلّ عنصرٍ فيها، ما الذي سنقوم به؟ ng-repeatرأينا سابقًا كيف تقوم Angular بإنشاء مجالٍ للعبارات لحمايتنا من إنشاء المتغيّرات في مجال JavaScript العام، وبطريقةٍ مماثلةٍ لذلك، يقوم التّوجيه ng-repeat بحمايتنا من الحالة التي رأيناها في المثال الأوّل، وذلك بإنشاء مجال Angular لكلّ دورة تكرار للحلقة. <ol ng-controller="ItemsController"> <li ng-repeat="item in items" ng-bind="item.name"></li> <li ng-bind="item.name"></li> </ol>الناتج ْ1. Item 1 2. Item 2 3. كما ترى، العنصر item غير متاحٍ خارج حلقة ng-repeat. تقوم ng-repeat بإنشاء مجالٍ ابنٍ جديد لكلّ عنصرٍ جديد من المجموعة مع الخصائص التي تحددها العبارة المحددة للمجموعة. في مثالنا السّابق، كان اسم الخاصّيّة هو item ولكن يمكن أن نستبدله بأيّ اسم. جرّب تغيير المثال السّابق وغيّر item إلى شيءٍ آخر، جرّب i أو x. عناصر الكائناتالتركيب النّحوي (مفتاح، قيمة) للكائن يسمح لك بالمرور على عناصر الكائنات. سيفيدك ذلك إن كنت تحتاج إلى كتابة محتوى كائنٍ ما بشكلٍ كامل في العرض. <table class="table table-condensed"> <tr ng-repeat="(propertyName, propertyValue) in {b: 'two', a: 1.0, c: 3}"> <td ng-bind="propertyName"></td> <td ng-bind="propertyValue"></td> </tr> </table>الناتج b two a 1 c 3ماذا لو أردنا استخراج عنصرٍ معيّن؟ كيف نقوم باستخراج عنصرٍ له اسمٌ محدّد من جميع الكائنات الموجودة في المجموعة؟ التركيب النحوي item in items الذي استخدمناه في مثال ng-repeat السابق، يشبه كثيرًا باني القائمة، ولكنّ ng-repeat لا تسمح للأسف بإرجاع أيّ شيءٍ غير عناصر الكائن أو المصفوفة في الطرف الأيمن للعبارة، لنحاول على أيّ حال. <ol ng-controller="ItemsController"> <!-- Invalid code! Syntax error, because 'for' is not supported! --> <li ng-repeat="item.name for item in items" ng-bind="item.name"></li> </ol> لم تعمل كما أخبرتك. باني القائمة الحقيقيّ يسمح لك بإرجاع أيّ شيء تريده من التعداد الأصليّ، عادةً باستخدام الكلمة المفتاحيّة for. تقدّم CoffeeScript مثالًا ممتازًا على ذلك. index$تقوم ng-repeat بإسناد الرقم التسلسلي للعنصر الحالي إلى متغيّرٍ خاصٍ هو index$ وذلك إضافةً إلى المتغيّر الّذي يحوي قيمة العنصر. في المثال التّالي سنقوم بإعادة اختراع العجلة، وسننشئ طريقةً جديدةً لكتابة قوائم HTML المرقّمة، حيث سنستخدم قيمة المتغيّر index$ في الإخراج. <div ng-controller="ItemsController"> <div ng-repeat="item in items"> {{$index + 1}}. {{item.name}} </div> </div>الناتج 1. Item 1 2. Item 2لنحاول الآن القيام باستخدامٍ متداخلٍ للتّوجيه ng-repeat. أوّلًا، لننشئ هيكل نموذج بياناتٍ أكثر تعقيدًا حيث يحوي كلّ عنصرٍ في الطبقة العلويّة مصفوفةً من الأبناء. function ItemsController($scope) { $scope.items = [ {name: 'Item 1', items: [ {name: 'Nested Item 1.1'}, {name: 'Nested Item 1.2'} ] }, {name: 'Item 2', items: [ {name: 'Nested Item 2.1'}, {name: 'Nested Item 2.2'} ] } ]; }والآن لنقم بإدخال حلقةٍ داخل أخرى، ستقوم الشّيفرة التّالية بإضافة اسم كلّ عنصرٍ إلى قائمةٍ مرقّمة. <div ng-controller="ItemsController"> <ol> <li ng-repeat="item in items"> {{item.name}} <ol> <li ng-repeat="item in item.items"> {{item.name}} </li> </ol> </li> </ol> </div>الناتج 1. Item 1 1. Nested Item 1.1 2. Nested Item 1.2 2. Item 2 1. Nested Item 2.1 2. Nested Item 2.2ولكن ماذا لو أردنا إنشاء عدّاداتٍ متداخلة؟ كيف سنمنع المتغيّر index$ الّذي تمّت تحديد قيمته في الحلقة الخارجيّة من أن يتمّ تظليله بالمتغيّر ذي الاسم نفسه الّذي تُنشئه الحلقة الدّاخليّة؟ ng-initربّما تتذكّر من الفصل الأول، المبادئ، أنّ Angular تسمح لنا بتهيئة متغيّرات المجال في منطقة العرض باستخدام التّوجيه ng-init. حلّ مشكلتنا يكمُن في استخدام ng-init للقيام بإعادة تعيين، أو عمل اسمٍ مستعارٍ، لمتغيّر الحلقة الخارجيّة index$ قبل أن يتمّ تظليله. <div ng-controller="ItemsController"> <div ng-repeat="item in items" ng-init="outerCount = $index"> {{outerCount + 1}}. {{item.name}} <div ng-repeat="item in item.items"> {{outerCount + 1}}.{{$index + 1}}. {{item.name}} </div> </div> </div>الناتج 1. Item 1 1.1. Nested Item 1.1 1.2. Nested Item 1.2 2. Item 2 2.1. Nested Item 2.1 2.2. Nested Item 2.2لا تكتفي ng-repeat بالمتغيّر index$ فهي تقوم بإسناد عددٍ من المتغيّرات البوليانيّة في كلّ دورٍ للحلقة، وهي: first$ وmiddle$ وlast$ وeven$ وodd$. يمكنك تجربة كلِّ واحدٍ منها ضمن المثال التّالي، وهذا المثال يستخدم التّوجيه المفيد ng-class حيث يتمّ وسم الخانة باللون الأخضر عندما تكون العبارة الشّرطيّة محقّقة. هل يمكنك اكتشاف الطّريقة الّتي تجعل العنصرين الأوّل والأخير فقط موسومين باللون الأخضر؟ (ملاحظة: ستحتاج إلى إضافة العمليّة ! فقط.) <ol> <li ng-repeat="val in [1,2,3,4,5]"> <span class="label label-default" ng-class="{'label-success': $middle}"> {{val}} </span> </li> </ol>هل لاحظت شيئًا عند استخدام المتغيّرين even$ وodd$؟ تقوم Angular بتحديد قيمة المتغيّرات وفق الطّريقة التّقليديّة التي تعتمد بدء التّرقيم من الصّفر في حلقات for. قد لا يبدو الأمر مثيرًا للاهتمام للوهلة الأولى، ولكن لو لاحظت جيّدًا فترقيم العناصر يبدأ من الصّفر بينما يبدأ ترقيم القائمة المرتّبة من الواحد، وهذا يعني أنّ المتغيّرين even$ وodd$ سيعملات بشكلٍ معاكسٍ لاسمهما بالنّسبة لترقيم القائمة. التفردكملاحظةٍ جانبيّة، يجب عليك الانتباه عند استخدام المتغيّرات البدائيّة (primitives) داخل ng-repeat بأن لا تكون جميع عناصر المجموعة متفرّدة، أي أنّه لا يوجد أي تكرار لأي قيمة في المجموعة، ومن يحدّد تطابق العناصر هو عمليّة المساواة المتشدّدة === في JavaScript. المساواة المتشددةلنقم ببعض التّجارب لإنعاش معلوماتنا في كيفيّة عمل عمليّة المساواة الصّارمة === في JavaScript. يختبر القالب التّالي المساواة بين أعدادٍ، سلاسل نصّيّةٍ وكائنات. <table class="table table-condensed"> <tr> <td>1 === 1</td> <td>{{1 === 1}}</td> </tr> <tr> <td>'1' === '1'</td> <td>{{'1' === '1'}}</td> </tr> <tr> <td>1 === '1'</td> <td>{{1 === '1'}}</td> </tr> <tr> <td>{} === {}</td> <td>{{ {} === {} }}</td> </tr> <tr> <td>{name: 1} === {name: 1}</td> <td>{{ {name: 1} === {name: 1} }}</td> </tr> </table>الناتج 1 === 1 true '1' === '1' true 1 === '1' false {} === {} false {name: 1} === {name: 1} falseبيّنّا بأنّ التّساوي بين عناصر المجموعة مرفوض، وسيبيّن المثال التّالي الخطأ الّذي سينتج عن تكرار العدد 1 في المجموعة. <ol> <!-- Invalid code! Duplicate element error, because '1' is repeated! --> <li ng-repeat="val in [1,2,1]" ng-bind="val"></li> </ol>قم بتعديل المثال السّابق، واستخدم خليطًا من الأعداد والسّلاسل النّصّيّة بدلًا من الأعداد فقط، جرّب المصفوفة التّالية [1,2,'1']. ماذا كانت النّتيجة؟ والآن حاول تغييرها باستخدام الكائنات بدلًا من الأعداد، جرّب المصفوفة التّالية [{name: 1},{name: 2},{name: 1}]. ستحتاج إلى تغيير العبارة "ng-bind="val إلى "ng-bind="val.name لتتمكّن من رؤية القيم. track byإنّ حلّ مشكلة مصفوفة الأعداد السّابقة يكمُن في إضافة تعليمة track by إلى عبارة ng-repeat وذلك لكي يتمّ تجاوز اختبار المساواة الّذي يتمّ القيام به تلقائيًّا للعناصر. يمكنك تتبّع عناصر المجموعة باستخدام أيّ متغيّر متفرّد (لا تتكرّر القيم الّتي يأخذها عند كلّ عنصر)، وإن لم يكن لديك متغيّر متفرّد (فالقيم العدديّة الموجودة داخل المصفوفة ليست كذلك)، يمكنك استخدام المتغيّر index$. <ol> <li ng-repeat="val in [1,2,1] track by $index" ng-bind="val"></li> </ol>الناتج 1. 1 2. 2 3. 1يجب عليك استخدام متغيّرات متفرّدة في نماذج كائناتك كلّما أمكنك ذلك، كأن تستخدم معرّفًا رقميًّا متفرّدًا يتمّ إنشاؤه عن طريق مخزن بيانات في الـbackend أو عن طريق نوعٍ ما من مولّد UUID في طرف المستخدم. إن لم يكن لديك مفرٌّ من استخدام المتغيّر index$ فكن حذرًا لأنّ التّغييرات في المجموعة قد تٌسبّب مشاكل في الأحداث الخاصة بالصفحة. توابع الاستدعاء الخلفيتسهّل Angular الحصول على مرجعٍ لعنصرٍ ما في المجموعة لتتمكّن من استخدامه داخل متحكّم ما. ليس عليك إلّا أن تمرّر عنصر المجموعة إلى تابع استدعاءٍ خلفيّ (callback) في التّوجيه الذي يسمح بتمرير تابع استدعاءٍ خلفيّ، مثل ng-click. على سبيل المثال، لنقم بكتابة شيفرة للسماح للمستخدم بحذف أحد عناصر المجموعة، سنحتاج إلى تعريف تابع استدعاء خلفيّ في المتحكّم بحيث يقبل مرجع العنصر المُراد حذفه كوسيطه الوحيد. يمكننا تسمية هذا التّابع بأي اسم، لنقم بتسميته destroy (سيكون هذا ملائمًا له). function ItemsController($scope) { $scope.items = [ {id: 1, name: 'Item 1'}, {id: 2, name: 'Item 2'}, {id: 3, name: 'Item 3'}, {id: 4, name: 'Item 4'} ]; $scope.destroy = function(item) { var index = $scope.items.indexOf(item); $scope.items.splice(index, 1); }; }قد تبدو طريقة حذف العنصر من المصفوفة في الشّيفرة السابقة مزعجة بعض الشيء إلا أنها الطريقة المُتّبعة في JavaScript للقيام بذلك، ولن تُسبّب أي خطأ مع Angular في ذلك. كُلّ ما بقي هو إضافة "(ng-click="destroy(item إلى عنصرٍ ما داخل الحلقة. كما يشير التّوجيه ng-click، فالزر هو الخيار الأفضل، ولكن لا بدّ من الإشارة إلى أنّه يمكننا استخدام التّوجيه ng-click مع أيّ عنصرٍ قابلٍ للنّقر. <div ng-controller="ItemsController"> <h4 ng-pluralize count="items.length" when="{'one': '1 item', 'other': '{} items'}"> </h4> <table class="table table-condensed"> <tr ng-repeat="item in items"> <td ng-bind="item.name"></td> <td> <button class="btn btn-xs btn-default" ng-click="destroy(item)"> destroy </button> </td> </tr> </table> </div>أعتبرُ طريقة Angular في ربط تابع الاستدعاء الخلفيّ destroy مثالًا واضحًا على الأسلوب التّصريحيّ الذي تتبعه Angular. كفائدةٍ إضافيّة، يعرض المثال السّابق استخدام التّوجيه ng-pluralize لكتابة نصٍّ يميّز بين المفرد والجمع بشكلٍ مشروط، حسب عدد العناصر ضمن مجموعة ما. إعداد هذا التّوجيه صعبٌ قليلًا ولكنك قد تحتاج لاستخدامه. start- وend-رغم أنه ليس شائعًا، إلّا أنك قد ترغب بإخراج عناصر "إخوة" لكلّ عضوٍ في المجموعة. وكمثالٍ على ذلك قائمة الوصف، أو العنصر dl، الذي يحوي زوجي العناصر dt وdd. وهنا تظهر المشكلة، فالتّوجيه ng-repeat مُصمّمٌ ليتمّ تطبيقه على عنصر HTML واحد. ولكنّ حلّ هذه المشكلة هو بتوسيع نطاق التنفيذ لهذا التّوجيه ليشمل العديد من عناصر HTML باستخدام اللاحقتين start- وend-. <dl ng-controller="ItemsController"> <dt ng-repeat-start="item in items">name</dt> <dd ng-bind="item.name"></dd> <dt>price</dt> <dd ng-repeat-end ng-bind="item.price"></dd> </dl>هناك العديد من التّوجيهات الأخرى التي تستخدم اللاحقتين start- وend- وليس التّوجيه ng-repeat فقط، ويجب عليك أن تتأكد من عدم انتهاء التّوجيهات المخصصة التي تقوم بإنشائها (كما ستتعلم في فصل التوجيهات اللاحق) بأيٍّ من اللاحقتين start- وend-. خلاصةتدعم Angular المجموعات بقوّةٍ ومرونة عن طريق التّوجيه ng-repeat، وتسمح لنا ببناء واجهة مستخدمٍ سريعة لتطبيقات CRUD. تنتهي في هذا الفصل نظرتنا العامّة للوظائف الغنيّة في تطوير الويب التي يمكننا تعلّمها دون الغوص كثيرًا في بنية Angular الدّاخليّة. ولكن من الآن فصاعدًا، سنبدأ بالغوص أعمق فأعمق في Angular الموسّعة، ولنتمكّن من القيام بذلك سيكون علينا تعلُّم كيفيّة قيام Angular بإدارة المكوّنات. وسيغطّي الفصل القادم النّظام الّذي طوّرته Angular لذلك، الوحدات. ترجمة وبتصرّف للفصل الخامس من كتاب: Angular Basics لصاحبه: Chris Smith.
×
×
  • أضف...