برمجة إكسيلتحليل البيانات

كيفية حساب القيم الفريدة في نطاق باستخدام VBA

دليل أكاديمي شامل يشرح كيفية حساب عدد القيم الفريدة والمتميزة داخل نطاق محدد في إكسيل باستخدام لغة البرمجة VBA وكائن القاموس Scripting.Dictionary.

Mohammed looti أكاديمي وباحث متخصص في علم النفس
تاريخ النشر
تمت المراجعة العلمية · د. مروة عبد العظيم · 12 سبتمبر، 2026
مراجعة وتدقيق علمي معتمد تاريخ التدقيق: 12 سبتمبر، 2026
د. مروة عبد العظيم دكتوراه
أستاذة علم النفس جامعة كربلاء
معايير التدقيق والاعتماد السريري

يخضع هذا المحتوى لمعايير ضبط الجودة والتدقيق العلمي والأكاديمي الصارمة في شبكة علم النفس العربي، لضمان صحة المعلومات ودقتها السريرية ومطابقتها لأحدث الأدلة والبراهين الصادرة عن الجمعيات النفسية والطبية المعتمدة (APA / WHO).

تمثل معالجة البيانات وتحليلها في بيئة برمجيات الجداول الحسابية، وعلى رأسها برنامج مايكروسوفت إكسيل (Microsoft Excel)، ركيزة أساسية من ركائز اتخاذ القرار في مختلف المؤسسات المالية والتقنية والبحثية. ومع تزايد حجم البيانات وتعقد هياكلها، بات الاعتماد على الدوال الصيغية التقليدية عاجزاً عن تلبية متطلبات الأداء السريع والمعالجة الذاتية المعقدة، لا سيما عندما يتعلق الأمر بإحصاء العناصر غير المكررة ضمن نطاقات متباينة الأبعاد والأحجام. تبرز هنا لغة Visual Basic for Applications، المعروفة اختصاراً بـ VBA، بوصفها أداة أتمتة فائقة القدرة تمنح المطورين والمحللين تحكماً دقيقاً في مسار معالجة الذاكرة وتدفق البيانات المنطقي على مستوى الخلية والمصفوفة.

تكتسب مسألة حساب القيم الفريدة داخل نطاق محدد أهمية استثنائية في تنقية البيانات، والتحقق من صحة المدخلات، وتجريد المتغيرات الإحصائية من شوائب التكرار التي تؤثر سلباً على المقاييس العامة كالارتباط والتوزيع التكراري والانحراف المعياري. وفي هذا السياق، لا يقتصر دور المبرمج على استخراج رقم مجرد يمثل عدد القيم، بل يمتد إلى فهم الطبيعة الهيكلية للبيانات النصية والرقمية، وإدراك الفروق العميقة بين الكائنات البرمجية المسؤولة عن التخزين المؤقت، مثل كائن القاموس Scripting.Dictionary وكائن المجموعات VBA Collection، والقدرة على توظيف خوارزميات الفرز والمقارنة المباشرة داخل ذاكرة الوصول العشوائي للوصول إلى أعلى معدلات الكفاءة البرمجية.

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

1. مقدمة تأسيسية لمفهوم القيم الفريدة في بيئة إكسيل البرمجية

1.1 الفرق الدلالي بين القيم الفريدة والقيم المتميزة

في حقل تحليل البيانات ونظرية المجموعات الرياضية، يقع العديد من المحللين في فخ الخلط الدلالي بين مفهومين متباينين تماماً: القيم الفريدة الحصرية (Unique Values) والقيم المتميزة أو المنفصلة (Distinct Values). يُقصد بالقيمة الفريدة في التحليل الإحصائي الدقيق ذلك العنصر الذي لا يتكرر إلا مرة واحدة فقط داخل مجتمع العينة المدروس؛ فإذا تكررت القيمة مرتين أو أكثر، سقطت عنها صفة “الانفراد” الرياضي وخرجت من هذا التصنيف. في المقابل، تشير القيم المتميزة إلى قائمة المفردات المستقلة المكونة للمجموعة بعد إزالة كافة التكرارات الزائدة والإبقاء على نسخة واحدة ممثلة لكل قيمة مكررة. على سبيل المثال، إذا احتوى نطاق معين على العناصر [أ، ب، أ، ج، د، ب]، فإن القيم الفريدة الصرفة هي [ج، د] وعددهما اثنان فقط، في حين أن القيم المتميزة هي [أ، ب، ج، د] وعديدها أربعة عناصر.

يترتب على هذا التمايز الدلالي أثر بالغ في النمذجة الرياضية والتحليل الإحصائي المتقدم. فعند بناء نماذج التنبؤ أو حساب المؤشرات الحيوية مثل متوسط القيمة لكل عميل، يؤدي الخطأ في تصنيف التكرارات إلى تشويه التوزيع الاحتمالي، وتقديم تقديرات مضللة حول اتساع قاعدة العينة الفعلية. إن تحديد التكرارات ومعالجتها قبل تغذية النماذج بالبيانات يعد خطوة حتمية في هندسة الخصائص وتنقية البيانات الأولية، إذ يسمح بعزل العناصر الشاذة وقياس درجة التجانس داخل المتغيرات المستقلة والتابعة على حد سواء.

وهنا يبرز الدور المحوري للغة فيجوال بيسك للتطبيقات (VBA) في أتمتة هذه العمليات المعقدة؛ فالأدوات التقليدية قد تفرض قيوداً صارمة على سرعة المعالجة أو تتطلب تدخلات يدوية متكررة لإعادة تعريف الشروط وفرز البيانات. تتيح VBA للمطورين صياغة خوارزميات مخصصة تميز بدقة متناهية بين المنطق الإحصائي للقيم الفريدة والمنطق التجزيئي للقيم المتميزة، وتطبيق القواعد المنطقية المناسبة بحسب متطلبات المسألة الحسابية بدقة متناهية ودون إهدار لموارد الجهاز.

1.2 محدودية الدوال القياسية ودواعي الانتقال إلى البرمجة

على الرغم من التطور الكبير الذي شهدته صيغ إكسيل الحسابية وظهور دوال حديثة متخصصة مثل الدالة UNIQUE في الإصدارات المدعومة بـ Dynamic Arrays، إلا أن الدوال التقليدية مثل COUNTIF مدمجة مع SUM أو SUMPRODUCT تظل تعاني من قصور جوهري عند معالجة البيانات الضخمة (Big Data Sets). تتطلب الصيغ المركبة التقليدية — مثل =SUM(1/COUNTIF(Range, Range)) — إجراء مقارنات تربيعية ذات تعقيد زمني من الدرجة O(N²)، حيث يقوم البرنامج بمقارنة كل خلية بجميع الخلايا الأخرى داخل النطاق، مما يؤدي إلى شلل تام في استجابة مصنف العمل عند تجاوز عدد السجلات بضعة آلاف من الصفوف.

علاوة على ذلك، فإن الاعتماد الكثيف على الصيغ المركبة يثقل كاهل الذاكرة الحسابية لإكسيل، إذ يتم إعادة تقييم هذه الصيغ المتطايرة مع كل تغيير طفيف يطرأ على أي خلية داخل المصنف، وهو ما يُعرف بظاهرة استنزاف خيوط المعالجة المتعددة (Recalculation Overhead). وتزداد هذه المشكلة تفاقماً في بيئات العمل المشتركة أو عند ربط المصنفات بمصادر بيانات خارجية تتطلب تحديثاً مستمراً، حيث تتحول ورقة العمل إلى بيئة بطيئة الاستجابة ومعرضة للانهيار المفاجئ نتيجة نفاد موارد النظام المخصصة لواجهة المستخدم الرسومية.

يمثل الانتقال إلى برمجة VBA حلاً حاسماً لهذه المعضلات، حيث تنقل البرمجة ثقل المعالجة من واجهة المستخدم التفاعلية إلى كائنات الذاكرة المؤقتة المنفصلة. تتيح VBA مرونة لا نهائية للتحكم في التدفق المنطقي؛ حيث يمكن للمبرمج إيقاف تحديث الشاشة وتعطيل الحسابات التلقائية، واستخدام هياكل بيانات متقدمة كجداول التجزئة (Hash Tables) التي تضغط التعقيد الزمني إلى الدرجة O(N)، فضلاً عن إمكانية تطبيق شروط معقدة لتصفية النصوص والأرقام والرموز في خطوة برمجية واحدة تتفوق بمراحل على أعتى الدوال المصفوفية المدمجة.

1.3 الأساس النظري لمعالجة السلاسل النصية والأرقام

تعتمد محركات الجداول الحسابية، وإكسيل بوجه خاص، على معايير داخلية معقدة لتخزين وتحديد نوع البيانات المخزنة داخل الخلية. تتنوع هذه البيانات بين الأرقام الحقيقية المزدوجة الدقة (Double)، والتواريخ المخزنة كأرقام تسلسلية داخلية، والنصوص المخزنة بنظام سلاسل BSTR المتوافقة مع بنية Component Object Model. يفرض هذا التنوع الهيكلي تحديات جسيمة عند فحص القيم الفريدة؛ فالخلية التي تحتوي على الرقم 100 كرقم تختلف بنيوياً وذاكرياً عن الخلية التي تحتوي على القيمة “100” كنص، حتى وإن تطابقت صورتهما المرئية على الشاشة للمستخدم البسيط.

وتتعاظم التحديات المرتبطة بالاختلافات الدقيقة في التنسيق؛ إذ تتسبب المسافات البادئة واللاحقة، والفواصل العشرية المتغيرة بحسب الإعدادات الإقليمية لنظام التشغيل، ومحارف التوجيه الخفية (مثل علامات الانتقال إلى سطر جديد أو محارف اليونيكود غير القابلة للكسر)، في إفشال عمليات المطابقة المنطقية. فمن المنظور البرمجي، يُعتبر النص “أحمد ” قيمة مختلفة كلياً عن النص “أحمد”، مما يترتب عليه احتساب العنصر مرتين كقيمتين متميزتين ما لم يسبق المقارنة إجراء عمليات تنقية ومعايرة مسبقة للمدخلات قبل الدخول في خوارزمية الفحص.

تقتضي المنهجية العلمية المتبعة برمجياً فحص كل عنصر بعناية عبر خطوات قياسية صارمة: تبدأ باستخلاص القيمة الصرفة المجردة من التنسيق الظاهري للخلية عبر الخاصية Value2 (التي تتفوق على الخاصية Value والخاصية Text بتجاوز معالجة تنسيقات العملات والتواريخ)، مروراً بتوحيد نوع المتغير، وصولاً إلى التحقق من وجوده الفريد عبر خوارزميات المقارنة البايتية أو النصية. تضمن هذه المقاربة الصارمة تحييد أثر التنسيق الخارجي للبيانات، وضمان أن عملية الإحصاء تعكس حقيقة المحتوى المعلوماتي المجرد للخلايا دون أدنى تشويه ناتج عن تباين واجهات الإدخال.

2. البنية البرمجية الأساسية لخوارزمية عد القيم الفريدة

2.1 النموذج القياسي للكود البرمجي ومكوناته الرئيسية

يرتكز النموذج المرجعي القياسي لحساب القيم الفريدة عبر لغة VBA على استغلال بنى التحكم التكرارية المقترنة بكائنات الذاكرة ذات قدرة التجزئة. يتألف الإجراء البرمجي الفرعي النموذجي من أربعة أركان أساسية: إعلان المتغيرات وتخصيص أنواعها، وتهيئة كائن التخزين المؤقت، والدوران المنطقي عبر خلايا النطاق المستهدف، وأخيراً استخلاص النتائج وعرضها للمستخدم بطريقة واضحة ومفهومة.

فيما يلي عرض متكامل للشفرة البرمجية المرجعية التي تعتمد على كائن القاموس Scripting.Dictionary لحساب القيم المتميزة داخل نطاق محدد من الخلايا:


Sub CountUniqueValuesReference()
    ' فرض الإعلان الصريح لكافة المتغيرات لضمان سلامة الذاكرة
    Dim targetRange As Range
    Dim cellIterator As Range
    Dim uniqueContainer As Object
    Dim totalUniqueCount As Long
    
    ' تحديد النطاق المستهدف ديناميكياً أو ثابتاً داخل ورقة العمل
    Set targetRange = Selection
    
    ' التحقق من صحة النطاق المحدد وعدم كونه خلية واحدة مفرغة
    If targetRange Is Nothing Then Exit Sub
    
    ' تهيئة كائن القاموس باستخدام تقنية الربط المتأخر لضمان التوافقية
    Set uniqueContainer = CreateObject("Scripting.Dictionary")
    uniqueContainer.CompareMode = 1 ' مقارنة نصية تتجاهل حالة الأحرف
    
    ' حلقة الدوران التكراري للمرور على كافة خلايا النطاق
    For Each cellIterator In targetRange
        If Not IsEmpty(cellIterator.Value2) And Not IsError(cellIterator.Value2) Then
            If Not uniqueContainer.Exists(cellIterator.Value2) Then
                uniqueContainer.Add cellIterator.Value2, True
            End If
        End If
    Next cellIterator
    
    ' استخلاص المجموع النهائي للقيم الفريدة المحصاة
    totalUniqueCount = uniqueContainer.Count
    
    ' إفراغ الكائن من الذاكرة تفادياً لتسريب الموارد البرمجية
    Set uniqueContainer = Nothing
    
    ' عرض النتيجة النهائية للمستخدم عبر نافذة حوارية منسقة
    MsgBox "إجمالي عدد القيم المتميزة داخل النطاق المحدد هو: " & totalUniqueCount, _
           vbInformation + vbMsgBoxRtlReading, "نتائج التحليل البرمجي"
End Sub

تتجلى أهمية التوثيق المنهجي للشفرة البرمجية بالتعليقات في تسهيل مهام الصيانة المستقبلية والتدقيق الأكاديمي للبرمجيات؛ فالتعليقات لا تقتصر على شرح ماهية السطر البرمجي، بل تمتد إلى توضيح المسوغ المعماري لاتخاذ قرارات تقنية محددة، مثل سبب اختيار القيمة Value2 عوضاً عن Value، واستخدام الربط المتأخر لضمان عمل الكود عبر مختلف إصدارات مايكروسوفت أوفيس دون الحاجة لتحديث يدوي للمكتبات المرجعية.

2.2 آلية استدعاء الإجراء وتنفيذه داخل بيئة التطوير

تتطلب الاستفادة الكاملة من كود إحصاء القيم الفريدة معرفة الإجراءات القياسية لإدماج الشفرة داخل بيئة التطوير المتكاملة (VBA IDE). يبدأ المسار بالضغط على مفتاحي Alt + F11 من داخل مصنف إكسيل لفتح محرر الأكواد، يليه النقر على قائمة Insert ثم اختيار Module لإنشاء وحدة نمطية قياسية جديدة تتمتع بنطاق وصول عام (Public Scope). يتم لصق الكود داخل هذه الوحدة مع التأكد من سلامة الترميز وضبط اتجاه النصوص البرمجية.

لتسهيل استخدام الإجراء من قبل المحللين والمستخدمين غير التقنيين، يمكن ربط الماكرو بكائنات التحكم الرسومية داخل ورقة العمل. يتم ذلك من خلال التوجه إلى تبويب “المطور” (Developer Tab)، واختيار أداة “زر أمر” (Button Form Control) أو رسم شكل توضيحي، ثم النقر بالزر الأيمن للفأرة واختيار “تعيين ماكرو” (Assign Macro)، وتحديد الإجراء CountUniqueValuesReference. يتيح هذا الربط للمستخدم تحديد أي نطاق عشوائي في الجدول الحسابي ثم الضغط على الزر بضغطة واحدة للحصول على التقرير الإحصائي الفوري دون الحاجة للتعامل المباشر مع واجهة البرمجة.

وفي سياق تصحيح الأخطاء واختبار منطق التدفق، يمتلك المطور أداة قوية تتمثل في مفتاح التنفيذ المتدرج F8 (Step Into)، والذي يتيح تتبع سريان التنفيذ سطراً بسطر داخل المحرر. وبالتزامن مع نافذة المراقبة الفورية (Locals Window) ونافذة المراقبة المخصصة (Watch Window)، يمكن فحص محتويات كائن القاموس في كل دورة تكرارية، ورصد كيفية إضافة المفاتيح وتجاهل المكررات، مما يكفل اكتشاف أي خلل في تصنيف البيانات النصية أو معالجة الفراغات قبل نشر الحل في بيئة الإنتاج الفعلية.

3. التحليل المعمق لكائن القاموس Scripting.Dictionary

3.1 مفهوم كائن القاموس ومقارنته بالمصفوفات التقليدية

يُعد كائن القاموس Scripting.Dictionary أحد أقوى هياكل البيانات المتاحة لمطوري VBA في بيئة نظام التشغيل ويندوز. يستند هذا الكائن المعماري إلى مفهوم أزواج “المفتاح والقيمة” (Key-Value Pairs)، وهي بنية مشابهة تماماً لما يُعرف في علوم الحاسوب بـ “المصفوفات الترابطية” (Associative Arrays) أو “جداول التجزئة” (Hash Tables). وبخلاف المصفوفات التقليدية الخطية التي تتطلب البحث عبر الفهرسة الرقمية المتسلسلة من العنصر الأول إلى العنصر الأخير، يوفر القاموس وصولاً شبه لحظي إلى عناصره بتعقيد زمني يقارب O(1)، بغض النظر عن حجم البيانات المخزنة.

ينتمي كائن القاموس وظيفياً إلى مكتبة محرك البرمجة النصية لمايكروسوفت (Microsoft Scripting Runtime، المتمثلة في الملف الديناميكي scrrun.dll). ويمكن استدعاؤه برمجياً بطريقتين مختلفتين تماماً في فلسفة الإدارة: الربط المبكر (Early Binding) والربط المتأخر (Late Binding). يتطلب الربط المبكر تفعيل المرجع يدوياً من قائمة Tools -> References داخل محرر الأكواد، مما يمنح المطور ميزة الإكمال التلقائي للتعليمات (IntelliSense) والتحقق من صحة الأنواع أثناء وقت الترجمة البرمجية (Compile-time Type Checking)، ويوفر زيادة طفيفة للغاية في سرعة التهيئة الأولية للكائن.

على الجانب الآخر، يعتمد الربط المتأخر على استدعاء الدالة العامة CreateObject("Scripting.Dictionary") وقت التشغيل الفعلي (Run-time). تكمن الميزة الاستراتيجية للربط المتأخر في مرونته الفائقة؛ إذ يضمن تشغيل المصنف البرمجي على أي حاسوب آخر دون الاصطدام بأخطاء المراجع المفقودة (Broken References Error) التي تنشأ عادةً عن اختلاف إصدارات نظام التشغيل أو مسارات المكتبات، مما يجعله الخيار المفضل لتطوير أدوات العمل المؤسسية القابلة للنشر العام.

وجه المقارنة كائن القاموس (Scripting.Dictionary) المصفوفات التقليدية (VBA Arrays)
آلية التخزين والوصول أزواج مفاتيح وقيم مع فهرسة عبر التجزئة (Hash Indexing). عناصر خطية مفهرسة رقمياً بأرقام صحيحة متسلسلة.
التعقيد الزمني للبحث ثابت O(1) في الحالات الطبيعية للبحث المباشر عن المفتاح. خطي O(N) يتطلب حلقة دوران للمرور على كافة العناصر.
التحقق التلقائي من التكرار مبني جوهرياً؛ لا يقبل تكرار المفاتيح إطلاقاً. يتطلب برمجة خوارزميات فحص يدوية معقدة ومرهقة للذاكرة.
التوافقية عبر المنصات مقتصر على نظام ويندوز (يتطلب مكتبة خارجية لنظام Mac). مدعوم بالكامل ومدمج أصلياً عبر كافة أنظمة التشغيل.

3.2 خاصية المفاتيح الفريدة كآلية تصفية تلقائية

تقوم الفلسفة التشغيلية لكائن القاموس على قاعدة صارمة لا تقبل الاستثناء: “لا يمكن وجود مفتاحين متطابقين في نفس الكائن في وقت واحد”. هذه الخاصية البنيوية تحول القاموس من مجرد وعاء لتخزين المتغيرات إلى مرشح ميكانيكي آلي فائق السرعة لتصفية التكرارات. فعند محاولة إدخال قيمة موجودة مسبقاً، يرفض الكائن العملية إما بإثارة خطأ تشغيلي (في حال استخدام دالة الإضافة الصريحة) أو بتجاهل التكرار وتحديث القيمة المقترنة تلقائياً (في حال استخدام أسلوب الإسناد المباشر عبر الأقواس).

في تطبيقات عد القيم الفريدة، لا يحتاج المطور إلى تخزين بيانات ذات وزن ذاكري داخل جزء “القيمة” (Value) المقترن بالمفتاح؛ إذ ينصب الهدف بالكامل على حصر المفاتيح ذاتها. وبناءً على ذلك، يتم استغلال خاصية عدم قبول التكرار من خلال تمرير عنصر فارغ أو قيمة منطقية خفيفة مثل True أو حتى سلسلة نصية فارغة vbNullString لتكون هي القيمة المقترنة، مما يرشد استهلاك الذاكرة العشوائية إلى أدنى حدوده الممكنة، لاسيما عند معالجة مئات الآلاف من السجلات المتتالية.

وعلاوة على حساب المجموع الكلي، يوفر كائن القاموس ميزة تحليلية بالغة الأهمية تتمثل في الدالة Keys، التي تقوم باستخراج كافة المفاتيح الفريدة وتفريغها دفعة واحدة داخل مصفوفة أحادية البعد. يفتح هذا السلوك آفاقاً واسعة للتحليلات اللاحقة؛ حيث لا يكتفي المبرمج بمعرفة عدد القيم المتميزة، بل يمكنه استرجاع القائمة المنقاة بالكامل بلمسة برمجية واحدة لإعادة كتابتها في جداول الإخراج أو تغذية عناصر التحكم في النماذج الرسومية (ListBoxes و ComboBoxes) دون الحاجة للمرور بحلقات تكرارية جديدة.

3.3 دوال وخصائص القاموس الأساسية في المعالجة

يوفر كائن القاموس حزمة متكاملة من الدوال والخصائص المصممة هندسياً لإدارة البيانات المخزنة والتحكم في دورة حياتها. تأتي في مقدمة هذه الأدوات الدالة المنطقية Exists(Key)، والتي تُعد الركيزة الأهم للفحص الوقائي؛ إذ تستقبل مفتاحاً معيناً وترجع قيمة منطقية (Boolean) تفيد بوجود العنصر مسبقاً من عدمه. يسمح استخدام هذه الدالة للمطور ببناء مسار شرطي مرن يمنع الاصطدام بالأخطاء البرمجية الناتجة عن محاولة إضافة مفاتيح مكررة عبر الإجراء Add.

أما الإجراء Add(Key, Item)، فهو الوسيلة الرسمية الصريحة لحقن البيانات في هيكل القاموس، ويتطلب تمرير معاملين إجباريين: المفتاح المراد اختباره، والعنصر المرتبط به. يجدر التنبيه إلى أن محاولة استدعاء هذا الإجراء بمفتاح مسجل مسبقاً ستؤدي حتماً إلى إثارة خطأ وقت التشغيل رقم 457 (“هذا المفتاح مقترن بالفعل بعنصر من هذه المجموعة”). ولتفادي هذا الخطأ، يمكن للمطور إما تغليف الإجراء بشرط Exists كما مر بنا، أو الاستعاضة عنه بالإسناد الضمني كالتالي: uniqueContainer(Key) = Empty، حيث يقوم هذا التعبير بإضافة المفتاح إذا كان جديداً، أو الكتابة فوق قيمته بصمت إذا كان موجوداً، محققاً هدف حصر القيم الفريدة بكفاءة واختصار.

تعتبر الخاصية Count هي الأداة الرياضية المباشرة التي يبحث عنها مطور إكسيل في سياق هذا المقال؛ حيث تُرجع هذه الخاصية في جزء من الثانية عدداً صحيحاً طويلاً (Long) يمثل الإجمالي الدقيق لعدد المفاتيح الفريدة المحتواة في القاموس في تلك اللحظة بالذات. وأخيراً، يتيح الإجراء RemoveAll تفريغ بنية القاموس ومسح كافة محتوياته بضربة واحدة، مما يعيد تعيين الكائن لحالته الصفرية ليكون جاهزاً لإعادة الاستخدام في جولات برمجية تالية دون الحاجة لتدمير الكائن وإعادة إنشائه من الصفر، مما يرفع من جودة استغلال موارد النظام.

4. التفكيك المنهجي والتحليل السطري للبرنامج النصي

4.1 إعلان المتغيرات وتخصيص مساحات الذاكرة

تبدأ الممارسة البرمجية المحترفة في بيئة VBA بفرض الانضباط الذاكري عبر تصدير الوحدة النمطية بالعبارة الإلزامية Option Explicit في السطر الأول. تجبر هذه التعليمة المترجم على حظر استخدام أي متغير غير معلن صراحة، مما يمنع وقوع الأخطاء الإملائية الشائعة في أسماء المتغيرات، والتي قد تتسبب في إنشاء متغيرات ضمنية فارغة من نوع Variant تستهلك قدراً كبيراً من الذاكرة وتؤدي إلى نتائج إحصائية خاطئة يصعب اكتشافها.

يتم تخصيص المتغير targetRange ككائن نطاق من نوع Range ليمثل مساحة العمل الفعلية داخل ورقة إكسيل، بينما يخصص المتغير cellIterator ليكون مؤشراً متحركاً من النوع ذاته للمرور على الخلايا الفردية. ومن الضروري جداً تخصيص المتغير العددي totalUniqueCount من نوع الأعداد الصحيحة الطويلة Long (وليس Integer)، نظراً لأن نوع Integer مقيد بحد أقصى قدره 32,767 قيمة، وهو رقم يتجاوزه حجم البيانات في جداول إكسيل الحديثة بسهولة فائقة، مما قد يعرض البرنامج لخطأ طفحان الذاكرة الحسابية (Overflow Error 6).

يتم بعد ذلك استدعاء الدالة CreateObject("Scripting.Dictionary") وتمرير نتيجتها إلى المتغير الكائني العام. يخصص نظام التشغيل ويندوز في هذه اللحظة مساحة من الذاكرة الديناميكية (Heap Memory) لاحتواء جدول التجزئة الداخلي للقاموس، ويهيئ مصفوفة المؤشرات التي ستتولى إدارة الارتباطات السريعة بين القيم المدخلة وعناوين تخزينها، مع ضمان عزل هذه المساحة عن فضاء الذاكرة المشغول بعمليات ورقة العمل الأساسية.

4.2 هندسة حلقة التكرار For Each وقواعد التحقق الشرطي

تعتبر حلقة التكرار For Each...Next الخيار النموذجي والأنسب للمرور على كائنات المجموعات والخلايا في VBA مقارنة بالحلقات الرقمية التقليدية For i = 1 To N. توفر هذه الحلقة تجريداً كلياً للأبعاد؛ حيث لا يحتاج المطور إلى حساب عدد الصفوف والأعمدة للنطاق يدوياً أو تتبع الإحداثيات الثنائية (Row, Column)، بل يتكفل المحرك البرمجي الداخلي بالانتقال التسلسلي بين خلايا النطاق، سواء كان النطاق عموداً منفرداً، أو صفاً أفقياً، أو كتلة جدولية متكاملة متعددة الأبعاد.

تخضع كل خلية تمر بها الحلقة لفحص منطقي صارم متعدد المراحل يبدأ بالتحقق من القيمة الصرفة عبر التعبير الشرطي:

If Not IsEmpty(cellIterator.Value2) And Not IsError(cellIterator.Value2) Then
يضمن هذا السطر استبعاد الخلايا الخالية تماماً من الحساب حتى لا تحتسب كقيمة فريدة (ما لم يكن ذلك مقصوداً في التصميم الإحصائي)، كما يمنع انهيار البرنامج عند مواجهة خلايا تحتوي على أخطاء حسابية ناجمة عن معادلات سابقة في ورقة العمل.

عند اجتياز الفحص الأولي، يأتي دور الشرط الجوهري للقاموس:

If Not uniqueContainer.Exists(cellIterator.Value2) Then uniqueContainer.Add cellIterator.Value2, True
يقوم كائن القاموس بحساب دالة التجزئة التشفيرية لمحتوى الخلية ومقارنتها مع المفاتيح المخزنة في ثوانٍ معدودة. فإذا تبين عدم وجودها، يتم حجز مساحة جديدة للمفتاح وتسجيل القيمة True كعنصر تابع لا قيمة حقيقية له سوى استيفاء متطلبات البنية؛ أما إذا كان المفتاح مسجلاً، تتخطى الحلقة السطر الشرطي وتنتقل مباشرة للخلية التالية عبر تعليمة Next cellIterator دون إهدار لدورات المعالجة.

4.3 استخراج الإحصائيات وإنهاء الإجراء البرمجي

بمجرد انتهاء الحلقة التكرارية من مسح كافة خلايا النطاق المستهدف، تنتقل السيطرة البرمجية إلى مرحلة الحصاد النهائي. يتم هنا الوصول مباشرة إلى الخاصية uniqueContainer.Count، وإسناد قيمتها الرقمية المباشرة إلى المتغير totalUniqueCount. تجري هذه العملية بصورة فورية تماماً؛ لأن كائن القاموس يحتفظ داخلياً بعداد مستمر يتم تحديثه تلقائياً مع كل عملية إضافة ناجحة، دون الحاجة للقيام بعملية إحصاء جديدة بعد اكتمال الحلقة.

تفرض قواعد البرمجة السليمة تحرير موارد النظام فور الانتهاء من العمل عبر إسناد الكائن إلى اللاشيء: Set uniqueContainer = Nothing. ورغم أن بيئة VBA تتضمن جامع قمامة آلي (Garbage Collector) يتولى تنظيف المتغيرات المحلية عند خروج الإجراء من نطاق التنفيذ، إلا أن التدمير الصريح للكائنات الخارجية، لا سيما كائنات COM، يضمن استعادة فورية لموارد الذاكرة المؤقتة من قبل نظام التشغيل، ويمنع تراكم المؤشرات المعلقة في الخلفية (Dangling Pointers).

تُختتم الشفرة البرمجية بتصميم واجهة التفاعل اللحظية للمستخدم؛ حيث يتم استدعاء الدالة القياسية MsgBox لعرض النتائج. ومن اللمسات الاحترافية المتقدمة في البيئة العربية، دمج الثوابت البرمجية vbInformation + vbMsgBoxRtlReading داخل نافذة الرسالة؛ لضمان محاذاة النص العربي من اليمين إلى اليسار وعرض علامات الترقيم والأرقام بصورة بصرية صحيحة تليق بالبرمجيات المؤسسية الموجهة لمتحدثي لغة الضاد.

5. التعامل مع حساسية حالة الأحرف والتنظيف الأولي للنصوص

5.1 التحكم في خاصية مقارنة النصوص CompareMode

أحد المزالق الكبرى في معالجة السلاسل النصية يكمن في الاختلاف الجوهري بين آليات المقارنة الثنائية والمقارنة النصية. يتحكم كائن القاموس في هذا السلوك من خلال الخاصية الجوهرية CompareMode، والتي تقبل قيمتين رئيسيتين: المقارنة الثنائية vbBinaryCompare (وقيمتها الرقمية 0)، والمقارنة النصية vbTextCompare (وقيمتها الرقمية 1). في حالة المقارنة الثنائية الافتراضية، يقارن المحرك البايتات الحرفية وفقاً لجدول ASCII أو UTF-16، مما يعني أن الحرف الإنجليزي الكبير “A” يُعتبر قيمة مختلفة تماماً ومستقلة عن الحرف الصغير “a”، وهو ما يؤدي إلى تضخيم غير مبرر في عدد القيم الفريدة في معظم التطبيقات الإدارية والمالية.

عند ضبط الخاصية على نمط المقارنة النصية CompareMode = 1، يغير القاموس خوارزمية التجزئة لتتعامل مع الأحرف الكبيرة والصغيرة بوصفها مفتاحاً واحداً، مما يؤدي إلى دمج النصوص اللاتينية المتشابهة تلقائياً بغض النظر عن حالة الأحرف المستخدمة في الإدخال. ويجب التأكيد بشكل حاسم على أن تعديل الخاصية CompareMode يجب أن يتم بصورة إجبارية قبل إضافة أول مفتاح إلى القاموس؛ إذ تمنع بنية COM البرمجية تعديل نمط المقارنة لكائن يحتوي بالفعل على بيانات، وستطلق خطأ وقت تشغيل فوري إذا تمت محاولة التعديل أثناء امتلاء القاموس.

يمتد هذا المفهوم إلى التعامل مع النصوص العربية؛ فعلى الرغم من أن اللغة العربية لا تحتوي على حروف كبيرة وصغيرة، إلا أنها تتميز بتنوع شكلي معقد في التنقيط ورسم الهمزات (مثل: أ، إ، آ، ا) وحروف الياء والألف المقصورة (ي، ى)، والتاء المربوطة والهاء (ة، هـ). لا تعالج المقارنة النصية التلقائية فروق الهمزات بدقة بمفردها، مما يفرض على المطور كتابة خوارزميات مساعدة لتوحيد النصوص العربية (Normalization) عبر استبدال الهمزات بحرف الألف المجرد قبل تمرير النص إلى مفاتيح القاموس، لضمان عد الكلمات المتشابهة كقيمة واحدة موحدة.

5.2 معالجة المسافات البيضاء والرموز الخفية

تعد المسافات البيضاء والرموز غير المرئية من ألد أعداء موثوقية التحليل الإحصائي في إكسيل. تنشأ هذه المشكلات عادةً عند استيراد البيانات من مصادر خارجية مثل قواعد بيانات SQL، أو ملفات النص المسطح (CSV)، أو منصات الويب وتخطيط الموارد (ERP). تتضمن هذه البيانات غالباً مسافات بادئة أو لاحقة غير مرئية للعين المجردة، تجعل القيمة “المحاسبة ” تظهر للمحلل مطابقة تماماً للقيمة “المحاسبة”، في حين يعتبرهما البرنامج النصي قيمتين فريدتين مختلفتين كلياً نظراً لاختلاف طول السلسلة النصية ورمز التجزئة المقابل.

لمواجهة هذا التحدي، تُعد الدالة Trim خط الدفاع الأول لتنظيف السلاسل النصية من المسافات الزائدة في البداية والنهاية. غير أن دالة VBA القياسية Trim تكتفي بإزالة رمز المسافة التقليدي الممثل برمز ASCII 32، وتفشل تماماً في التعرف على “المسافة غير القابلة للكسر” (Non-Breaking Space) الممثلة برمز ASCII 160 أو رمز اليونيكود ChrW(160)، والتي تُصدر بكثافة عند تصدير البيانات من متصفحات الويب وجداول HTML.

تقتضي أفضل الممارسات البرمجية تنقية النصوص تنقية مركبة عبر دمج الدالة Replace مع Trim قبل فحص القيمة، كما هو موضح في النموذج التالي:


Dim cleanedValue As String
cleanedValue = Trim(Replace(cellIterator.Value2, Chr(160), " "))
cleanedValue = Trim(Replace(cleanedValue, Chr(9), " ")) ' إزالة محارف التبويب Tab

إن تبني منهجية تنظيف وتنميط البيانات يرفع من الموثوقية الرياضية لعمليات الإحصاء بدرجة هائلة؛ إذ يحمي النماذج المؤسسية من أخطاء العد الزائف ويضمن أن تكون التقارير المرفوعة للإدارة العليا انعكاساً دقيقاً وحقيقياً للبيانات المجردة دون شوائب التنسيق الطباعي أو عيوب التصدير التقني.

6. إدارة الحالات الاستثنائية: الخلايا الفارغة والأخطاء البرمجية

6.1 استبعاد أو احتساب الخلايا الفارغة في النطاق

تطرح الخلايا الفارغة إشكالية فلسفية ومنهجية في علم البيانات: هل يعبر الفراغ عن “قيمة معدومة” تستحق الحساب بوصفها صنفاً مستقلاً، أم يمثل “غياباً للبيانات” يتطلب التجاهل التام؟ من المنظور البرمجي البحت داخل كائن القاموس، يتم التعامل مع السلسلة الفارغة "" أو المتغير الفارغ Empty كمدخل صالح ومقبول يمكن حيازته كمفتاح مستقل. وبالتالي، إذا مر الكود على عشرين خلية فارغة دون وضع شروط حظر، فإن القاموس سيضيف أول خلية فارغة كمفتاح ويحسبها كقيمة فريدة واحدة، ويتجاهل التسع عشرة خلية فارغة المتبقية.

يعتمد قرار احتساب الخلية الفارغة على طبيعة الغرض التحليلي. فإذا كان الهدف هو إحصاء الفئات الوظيفية المدخلة، فإن الخلية الفارغة تدل على سجل ناقص ويجب استبعادها. ويتم هذا الحظر البرمجي بفحص دقيق عبر دمج الدوال IsEmpty و Len لضمان استبعاد الخلايا التي لا تحتوي على أي بايت بيانات، وأيضاً الخلايا التي تحتوي على نصوص ذات طول صفري ناتجة عن معادلات إكسيل السابقة كـ ="":


If Not IsEmpty(cellIterator.Value2) And Len(Trim(CStr(cellIterator.Value2))) > 0 Then
    ' المعالجة البرمجية وإدراج المفتاح
End If

وعلى النقيض من ذلك، إذا كانت المسألة تتعلق بالتحقق من جودة إدخال البيانات، فإن ظهور الخلية الفارغة يُعتبر في حد ذاته حدثاً تحليلياً يستوجب الإحصاء لإعلام المحلل بوجود فئة “غير محددة”. وفي هذه الحالة، يمكن للمطور استبدال الفراغ بقيمة نصية صريحة كـ "[فارغ]" لتوثيق هذا المكون ضمن الإحصاء الفريد دون تشويه طبيعة المفاتيح الأخرى.

6.2 التعامل مع خلايا الأخطاء الحسابية والمفاهيمية

من أخطر الحالات الاستثنائية التي تواجه أكواد VBA عند قراءة خلايا النطاق هي اصطدام البرنامج بخلايا تحتوي على أخطاء حسابية ناجمة عن صيغ فاشلة في ورقة العمل، مثل #DIV/0!، #N/A، #VALUE!، #REF!، و #NAME?. عند محاولة الكود البرمجي قراءة خاصية cellIterator.Value أو Value2 لخلية تتضمن خطأ حسابياً وإسنادها إلى متغير نصي أو تمريرها كمعامل لدالة، يفشل المحرك في إجراء التحويل الضمني لنوع البيانات، وينهار البرنامج فوراً باعتراض خطأ وقت التشغيل رقم 13 (“عدم تطابق النوع” – Type Mismatch).

لضمان حماية الكود من الانهيار، يجب استخدام الدالة المدمجة IsError(cellIterator.Value) قبل الاقتراب من محتوى الخلية أو قراءته لأي غرض آخر. فإذا أرجعت الدالة القيمة True، يتوجب على الكود اتخاذ مسار تفادي فوري بتخطي الخلية واستبعادها من الحساب، مع إمكانية تسجيل إحداثيات الخلية الموبوءة بالخطأ في سجل أحداث مخصص لإشعار المستخدم بوجود بيانات تالفة داخل النطاق المحدد.

فضلاً عن الفحص الوقائي، تقتضي قواعد البرمجة الدفاعية (Defensive Programming) تضمين كتل معالجة الأخطاء المنظمة On Error GoTo ErrorHandler داخل الإجراء البرمجي بالكامل. يضمن هذا الإجراء أنه في حال حدوث أي خطأ فيزيائي في قراءة الذاكرة أو تعطل الاتصال بالكائنات، لا يتوقف إكسيل عن الاستجابة، بل ينتقل الكود بسلاسة إلى كتلة التنظيف ليقوم بإعادة تفعيل تحديث الشاشة، وتفريغ كائنات الذاكرة، وتقديم رسالة استيضاحية مهذبة تشرح للمستخدم سبب تعذر إتمام العملية البرمجية.

7. تطوير الكود للتعامل مع النطاقات الديناميكية ومتعددة الأبعاد

7.1 تحديد حدود النطاق برمجياً بشكل تلقائي

في بيئات العمل الواقعية، نادراً ما تكون النطاقات الحسابية ثابتة الأبعاد؛ فالجداول اليومية تخضع لعمليات توسع وانكماش مستمرة مع إضافة مبيعات جديدة أو حذف سجلات قديمة. إن بناء كود يعتمد على نطاق ثابت محدد مسبقاً، مثل Range("A1:A100")، يمثل عيباً هيكلياً يحد من كفاءة الحل. لذلك، يتعين على المطور بناء آليات ديناميكية تكتشف أطراف النطاق الفعلي وتتكيف مع حجم البيانات في وقت التشغيل بصورة آلية مستقلة.

تتمثل الطريقة البرمجية القياسية والأكثر أماناً لتحديد آخر صف مأهول بالبيانات في عمود معين في الصعود من أقصى قاع ورقة العمل باستخدام الخاصية End(xlUp)، وهو ما يحاكي اختصار لوحة المفاتيح الشهير Ctrl + Up Arrow. يتميز هذا الأسلوب بقدرته على تجاوز الخلايا الفارغة البينية التي تخدع دوال البحث الهابط التقليدية، كما يتضح من التركيب البرمجي التالي:


Dim lastRow As Long
Dim dynamicRange As Range
With ThisWorkbook.Sheets("DataSheet")
    lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
    If lastRow >= 1 Then
        Set dynamicRange = .Range("A1:A" & lastRow)
    End If
End With

VBA count unique
VBA count unique

وفي حال التعامل مع كتل جدولية مستطيلة متصلة تتضمن أعمدة وصفوفاً متعددة، تبرز الخاصية CurrentRegion بوصفها خياراً ممتازاً. تقوم هذه الخاصية بالتقاط كامل الكتلة المتصلة المحيطة بخلية معينة، متوقفة عند أول صف وعمود فارغين تماماً. يتيح ذلك للكود البرمجي استيعاب الجداول الكاملة وحساب القيم الفريدة عبرها دون الحاجة لتحديد عدد الأعمدة والصفوف بصورة يدوية، مما يضفي مرونة استثنائية على الإجراء البرمجي.

7.2 حساب القيم الفريدة عبر أعمدة متعددة متجاورة وغير متجاورة

تتجاوز متطلبات التحليل المتقدم مجرد حساب التكرارات في عمود فردي؛ إذ تتطلب العديد من الدراسات التحقق من تميز السجل المركب (Composite Record) المكون من قيم متفرقة عبر عدة أعمدة؛ مثل تحديد عدد “العملاء الفريدين في كل فرع”، حيث يمثل اسم العميل العمود الأول، ويمثل اسم الفرع العمود الثاني. في هذه الحالة، لا يُعد الاسم بمفرده عنصراً فريداً، ولا الفرع بمفرده، بل يشكل اتحادهما معاً المفتاح الفريد المركب المطلوب حسابه.

لتحقيق هذا الهدف برمجياً عبر لغة VBA، يتم استخدام تقنية “الدمج النصي المحمي بفواصل” (Delimited Concatenation). يقوم الكود بالدوران عبر صفوف النطاق، ودمج قيم الخلايا المحددة لكل صف في سلسلة نصية واحدة يفصل بينها رمز خاص نادراً ما يظهر في النصوص العادية (مثل محرف الأنبوب | أو علامة التلدة ~). يمنع استخدام هذه الفواصل التداخل العرضي بين النصوص المختلفة، مثل دمج “أحمد” و”علي” الذي يجب ألا يتطابق مع دمج “أحمدع” و”لي”:


Dim compositeKey As String
Dim rowIdx As Long
For rowIdx = 1 To dynamicRange.Rows.Count
    compositeKey = dynamicRange.Cells(rowIdx, 1).Value2 & "|" & _
                   dynamicRange.Cells(rowIdx, 2).Value2
    If Not uniqueContainer.Exists(compositeKey) Then
        uniqueContainer.Add compositeKey, True
    End If
Next rowIdx

تتيح هذه الهندسة البرمجية معالجة النطاقات متعددة الأبعاد غير المتجاورة أيضاً؛ حيث يمكن تمرير كائن نطاق مجزأ يتضمن أعمدة متباعدة (مثل Range("A:A, C:C, F:F"))، وتقوم الحلقات التكرارية بتفكيك مناطق النطاق عبر الخاصية Areas، وتطبيق خوارزمية الدمج والتجزئة بدقة متناهية، مما يمنح محلل البيانات أداة احترافية تضاهي إمكانيات الاستعلام المتقدم بلغة SQL في قواعد البيانات العلائقية.

8. البدائل البرمجية: كائن المجموعات VBA Collection

8.1 بنية كائن المجموعة Collection واستخدامه لحساب التميز

يُمثل كائن المجموعات VBA Collection البديل الأصلي والمدمج لكائن القاموس؛ إذ لا يتطلب استدعاء أي مكتبات خارجية أو إنشاء كائنات عبر محرك البرمجة النصية لنظام ويندوز. يتوفر كائن المجموعة بشكل قياسي ومباشر داخل النواة الأساسية للغة VBA عبر كافة المنصات، وهو كائن مخصص لتجميع عناصر مختلفة وتخزينها بترتيب تسلسلي مع إمكانية ربط كل عنصر بمفتاح نصي فريد يمنع التكرار.

تعتمد خوارزمية عد القيم الفريدة باستخدام كائن المجموعة على فكرة برمجية ذكية غير تقليدية: استخدام الاعتراض العمدي للأخطاء البرمجية كأداة للتحكم في التدفق المنطقي. فعند استدعاء الإجراء Collection.Add وتمرير القيمة المراد إحصاؤها في موضع المفتاح النصي (Key Parameter)، يفرض الكائن شرط عدم تكرار المفتاح بدقة صارمة. فإذا كان المفتاح مسجلاً بالفعل، يطلق الكائن خطأ وقت التشغيل رقم 457. ولتطويع هذا السلوك لصالحنا، يتم تفعيل التخطي الصامت للأخطاء عبر العبارة On Error Resume Next:


Sub CountUniqueUsingCollection()
    Dim myCol As New Collection
    Dim cell As Range
    Dim itemKey As String
    
    On Error Resume Next ' تعليق التوقف عند الأخطاء لتخطي التكرارات
    For Each cell In Selection
        If Not IsEmpty(cell.Value2) Then
            itemKey = CStr(cell.Value2) ' تحويل القيمة لنص إلزامي للمفتاح
            myCol.Add cell.Value2, itemKey
        End If
    Next cell
    On Error GoTo 0 ' إعادة التوجيه الطبيعي للأخطاء
    
    MsgBox "إجمالي العناصر الفريدة عبر Collection هو: " & myCol.Count
End Sub

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

8.2 المقارنة الفنية والتحليلية بين Collection و Scripting.Dictionary

على الرغم من النجاح العملي لكائن Collection في إحصاء القيم الفريدة، إلا أن التحليل الفني الأكاديمي يبرز فروقاً بنيوية جوهرية تجعل كائن القاموس Scripting.Dictionary هو المتفوق هندسياً في معظم السيناريوهات. تكمن نقطة الضعف الرئيسية في كائن Collection في افتقاره التام لدالة استعلام مسبقة موازية لـ Exists، مما يجبر المطور على إثارة أخطاء النظام البرمجية عمداً وتجاهلها. يمثل استدعاء معالجة الأخطاء عبئاً إضافياً على محرك التنفيذ، مما يترتب عليه تباطؤ ملحوظ في الأداء عند معالجة المصفوفات الضخمة التي تحتوي على مئات الآلاف من التكرارات.

بالإضافة إلى ذلك، يفرض كائن المجموعة قيوداً على المفاتيح؛ حيث يجب أن يكون المفتاح دائماً من النوع النصي String، ولا يقبل الأرقام الصرفة كمفاتيح مباشرة، مما يضطر المبرمج لإجراء عمليات تحويل صريحة مستمرة عبر الدالة CStr، وهو ما يزيد من زمن المعالجة. على النقيض من ذلك، يتعامل القاموس بمرونة مطلقة مع المفاتيح من مختلف الأنواع (أرقام، نصوص، تواريخ)، ويتيح إمكانية تفريغ المفاتيح مباشرة عبر الدالة Keys، وتعديل نمط حساسية الأحرف عبر CompareMode، وهي خواص محروم منها كائن Collection كلياً.

ومع كل ذلك، يتمتع كائن Collection بميزة استراتيجية فريدة لا يمكن إغفالها: التوافقية الشاملة عبر الأنظمة (Cross-Platform Compatibility). فمكتبة Scripting Runtime المسؤولة عن القاموس غير متوفرة على بيئات نظام التشغيل Mac (macOS)؛ وبالتالي، فإن أي كود يعتمد على القاموس سيفشل فوراً عند تشغيله على أجهزة آبل ماكنتوش. هنا يصبح كائن Collection هو الملاذ الوحيد الموثوق لمطوري VBA الذين يستهدفون نشر حلولهم البرمجية لتعمل بسلاسة وموثوقية عبر كافة أنظمة التشغيل دون استثناء.

9. التقنيات المتقدمة: معالجة المصفوفات في الذاكرة RAM

9.1 تحميل نطاق إكسيل مباشرة إلى مصفوفة ثنائية الأبعاد

في هندسة برمجيات إكسيل وتطوير الأداء العالي، توجد قاعدة ذهبية راسخة: “التفاعل مع واجهة إكسيل عبر كائنات النطاق (Range Objects) هو أبطأ العمليات البرمجية على الإطلاق”. ففي كل مرة يقرأ فيها الكود خاصية cellIterator.Value2، يضطر محرك VBA إلى إجراء عملية اتصال بيني عبر طبقة COM (COM Marshaling) لقراءة الخلية من واجهة التطبيق، مما يخلق عنق زجاجة تشغيلي حاد يبطئ من سرعة التنفيذ عند التعامل مع نطاقات تتألف من عشرات الآلاف من الصفوف.

تتمثل القفزة النوعية للتغلب على هذا التباطؤ في تقنية “التحميل الذاكري الدفعي” (Memory Ingestion). بدلاً من تفقد الخلايا خلية تلو الأخرى، يتيح محرك VBA نقل كامل بيانات النطاق الحسابي بلمسة واحدة مباشرة وتفريغها داخل مصفوفة ثنائية الأبعاد في ذاكرة الوصول العشوائي (RAM) عبر تعبير برمجي واحد فائق السرعة:


Dim dataMatrix As Variant
' قراءة النطاق بالكامل في جزء من الميلي ثانية إلى الذاكرة الصرفة
dataMatrix = targetRange.Value2

بمجرد إتمام هذا الإسناد، ينقطع الاتصال البرمجي مع واجهة ورقة العمل وتتحول المعالجة بالكامل إلى ذاكرة النظام الداخلية. يتم توجيه حلقة الدوران التكراري للمرور على عناصر المصفوفة dataMatrix(row, col) بدلاً من خلايا Range، مما يحقق طفرة في سرعة المعالجة تصل إلى عشرات الأضعاف مقارنة بالطريقة الكلاسيكية، مع تقليص استهلاك خيوط المعالجة في الواجهة الرسومية إلى الصفر تقريباً.

9.2 خوارزميات الفرز والمقارنة المتتالية في الذاكرة

بالإضافة إلى استخدام جداول التجزئة والكائنات الخارجية، يوفر التفكير الخوارزمي الصرف بديلاً متقدماً لإحصاء القيم الفريدة دون الاعتماد على أي كائنات وسيطة مثل Dictionary أو Collection، وذلك من خلال دمج خوارزميات الفرز السريع (QuickSort) مع تقنيات المقارنة المتتالية الأحادية (Linear Scan).

تقوم المنهجية على مرحلتين متتاليتين في الذاكرة:

  • المرحلة الأولى: استخراج قيم النطاق وتسطيحها في مصفوفة أحادية البعد، ثم تطبيق خوارزمية فرز سريعة مثل QuickSort لترتيب العناصر تصاعدياً أو تنازلياً بتعقيد زمني قدره O(N log N). يؤدي الترتيب إلى تجميع كافة القيم المتماثلة في مواضع متجاورة حتماً داخل المصفوفة.
  • المرحلة الثانية: تمرير حلقة فحص خطية واحدة بتعقيد O(N) لمقارنة كل عنصر بالعنصر الذي يليه مباشرة؛ فإذا اختلف العنصر الحالي عن سابقه، يُسجل ذلك كظهور لقيمة فريدة جديدة ويزداد العداد بمقدار واحد.

يوضح الجدول التالي تحليلاً مقارناً للتعقيد الحسابي بين الطرق البرمجية المختلفة:

المنهجية الخوارزمية التعقيد الزمني (Time Complexity) التعقيد المكاني (Space Complexity) التقييم التشغيلي
الدوال الصيغية المركبة (COUNTIF) تربيعي O(N²) O(1) ظاهرياً ولكن ثقيل في الذاكرة الحسابية غير عملي في البيانات الضخمة (بطيء جداً).
حلقة الخلايا مع القاموس (Dictionary + Cells) خطي O(N) مع ثوابت COM عالية O(U) حيث U عدد القيم الفريدة متوسط السرعة؛ يعاني من عنق زجاجة قراءة الخلايا.
مصفوفة الذاكرة مع القاموس (Array + Dictionary) خطي نقي O(N) O(N) للمصفوفة + O(U) للقاموس الأسرع على الإطلاق في بيئة ويندوز (الأداء الذهبي).
الفرز في الذاكرة (QuickSort + Array) شبه خطي O(N log N) O(N) لمساحة المصفوفة فقط ممتاز؛ مستقل تماماً عن أي كائنات أو مراجع خارجية.

تكتسب هذه الخوارزميات اليدوية جدواها الاستثنائية في بيئات الحوسبة الصارمة والأنظمة التي تفرض قيود أمان تمنع إنشاء كائنات COM خارجية (ActiveX Scripting Blocked)، كما توفر للباحثين والمطورين استقلالية معمارية كاملة تعتمد فقط على الإمكانيات الحسابية الخام لوحدة المعالجة المركزية (CPU).

10. تحليل الأداء المعياري والكفاءة الحسابية في البيانات الضخمة

10.1 قياس الزمن المستغرق باستخدام توابع توقيت النظام

في الممارسات الهندسية للبرمجيات، لا يتم قياس جودة الكود بمجرد صحة مخرجاته الرياضية فحسب، بل بكفاءته المعيارية في استهلاك الوقت وموارد الجهاز. لقياس زمن التنفيذ بدقة متناهية داخل بيئة VBA، يُعد التابع القياسي Timer الأداة الأساسية؛ حيث يُرجع هذا التابع عدداً فردياً يمثل عدد الثواني وأجزاء المئة من الثانية التي انقضت منذ منتصف الليل بتوقيت النظام الداخلي للحاسوب.

لإجراء تجربة أداء معيارية (Benchmarking) دقيقة، يتم التقاط التوقيت قبل بدء المعالجة مباشرة ثم طرحه من التوقيت المسجل فور اكتمال الخوارزمية، مع عزل زمن إدخال البيانات وزمن إظهار رسالة الإخراج للمستخدم لضمان قياس الجهد الخوارزمي الصرف:


Dim startTime As Double
Dim executionTime As Double

startTime = Timer
' استدعاء خوارزمية المعالجة الذاكرية وحساب التكرارات هنا
executionTime = Timer - startTime

Debug.Print "تم إنجاز التحليل الإحصائي في: " & Format(executionTime, "0.0000") & " ثانية."

تؤكد الاختبارات المعملية المجراة على مجموعات بيانات قياسية تتراوح بين 10,000 إلى 1,000,000 سجل أن أسلوب الجمع بين مصفوفات الذاكرة وكائن القاموس يسجل أزمنة قياسية تقل عن ثانية واحدة لمائة ألف سجل، في حين تستغرق الطرق المعتمدة على فحص الخلايا الفردية عبر الواجهة الرسومية أضعافاً مضاعفة من الوقت، بينما تنهار الدوال الحسابية التقليدية بعجز كامل عن الاستجابة في الاختبارات المليونية.

10.2 استراتيجيات تحسين سرعة الأكواد البرمجية

للوصول بالأكواد البرمجية إلى حدودها القصوى من الكفاءة والرشاقة، يجب تطبيق حزمة الإجراءات التطهيرية الأربعة لإدارة بيئة تطبيق إكسيل (Excel Application Tuning). تقوم هذه الاستراتيجية على تعليق العمليات الجانبية غير الضرورية التي ينفذها التطبيق تلقائياً في الخلفية أثناء تشغيل الماكرو، مما يحصر كامل قوة المعالج في خدمة خوارزمية الإحصاء:

  • تعطيل تحديث الشاشة: عبر إسناد القيمة Application.ScreenUpdating = False؛ لمنع إكسيل من إعادة رسم واجهة المستخدم الرسومية مع كل حركة أو معالجة للبيانات، وهو ما يوفر الجزء الأكبر من وقت المعالجة.
  • إيقاف الحساب التلقائي للصيغ: عبر الأمر Application.Calculation = xlCalculationManual؛ لضمان عدم قيام البرنامج بإعادة احتساب المعادلات الموزعة في المصنف بالتزامن مع قراءة النطاقات الكبيرة.
  • تعطيل معالجة الأحداث البرمجية: باستخدام Application.EnableEvents = False؛ لقطع الطريق على تشغيل أي ماكرو فرعي مربوط بأحداث تغيير الأوراق أو الخلايا أثناء دوران الكود الأساسي.
  • إلغاء فواصل الصفحات الطباعية: عبر ActiveSheet.DisplayPageBreaks = False؛ لمنع المحرك من حساب تخطيط الطابعة أثناء العمليات الذاكرية المكثفة.

ومن الأهمية بمكان التأكيد على المبدأ الهندسي الحاكم: “ما تم تعطيله يجب إعادته حتماً”. لذلك، يجب تنظيم الكود بحيث يتم استرجاع كافة الإعدادات الأصلية للتطبيق إلى حالتها التشغيلية الطبيعية (True و xlCalculationAutomatic) داخل كتلة الخروج الحتمية (Finally/Cleanup Block)، لضمان عدم ترك بيئة عمل المستخدم في حالة جمود بعد انتهاء تنفيذ البرنامج النصي.

11. تحويل الكود إلى دالة معرفة بواسطة المستخدم (UDF)

11.1 بناء الدالة المخصصة لاستخدامها مباشرة داخل الخلايا

يمثل تحويل الإجراءات الفرعية المستقلة (Subroutines) إلى دوال وظيفية معرفة بواسطة المستخدم (User-Defined Functions – UDF) نقلة نوعية في قابلية استخدام الحل البرمجي؛ إذ تتيح الدالة للمستخدم كتابة اسمها واستدعائها مباشرة داخل أي خلية في ورقة العمل تماماً كما يتعامل مع الدوال الرياضية القياسية مثل SUM أو VLOOKUP.

فيما يلي التصميم البرمجي المكتمل لدالة مخصصة فائقة الأداء لحساب القيم الفريدة داخل أي نطاق يتم تمريره كمعامل رسمي:


Public Function COUNT_DISTINCT(ByVal targetRange As Range) As Variant
    ' التحقق من صحة المدخلات ومنع الأخطاء في النطاقات الفارغة
    If targetRange Is Nothing Then
        COUNT_DISTINCT = 0
        Exit Function
    End If
    
    Dim memoryMatrix As Variant
    Dim uniqueDict As Object
    Dim rowIdx As Long, colIdx As Long
    Dim currentItem As Variant
    
    ' تحميل البيانات دفعة واحدة في مصفوفة ذاكرية للسرعة القصوى
    If targetRange.Cells.CountLarge = 1 Then
        If IsEmpty(targetRange.Value2) Or IsError(targetRange.Value2) Then
            COUNT_DISTINCT = 0
        Else
            COUNT_DISTINCT = 1
        End If
        Exit Function
    End If
    
    memoryMatrix = targetRange.Value2
    Set uniqueDict = CreateObject("Scripting.Dictionary")
    uniqueDict.CompareMode = 1 ' مقارنة نصية تتجاهل حالة الأحرف
    
    ' الدوران داخل مصفوفة الذاكرة الثنائية
    For rowIdx = 1 To UBound(memoryMatrix, 1)
        For colIdx = 1 To UBound(memoryMatrix, 2)
            currentItem = memoryMatrix(rowIdx, colIdx)
            If Not IsEmpty(currentItem) And Not IsError(currentItem) Then
                If Len(Trim(CStr(currentItem))) > 0 Then
                    uniqueDict(currentItem) = True ' إضافة فورية وتجاهل التكرار
                End If
            End If
        Next colIdx
    Next rowIdx
    
    ' إرجاع النتيجة الحسابية النهائية للخلية الحاضنة
    COUNT_DISTINCT = uniqueDict.Count
    Set uniqueDict = Nothing
End Function

لاستخدام هذه الدالة، يكتفي المستخدم بكتابة الصيغة التالية في شريط الصيغ: =COUNT_DISTINCT(A2:A50000)، لتقوم الدالة فوراً بمعالجة النطاق وإرجاع عدد المفردات الفريدة دون أي حاجة للضغط على أزرار الماكرو أو مغادرة بيئة العمل الحسابية المألوفة.

11.2 ضمان التفاعلية وإعادة الحساب التلقائي للدالة

تخضع الدوال المعرفة بواسطة المستخدم لمنظومة الحساب الشجري لإكسيل (Calculation Tree). وفي بعض السيناريوهات الخاصة، قد يطرأ تعديل على بيانات فرعية تؤثر على النطاق دون أن يشعر المحرك بضرورة إعادة حساب الدالة المخصصة. لحل هذه الإشكالية وجعل الدالة شديدة التفاعلية وحساسة لأي حركة في المصنف، يمكن للمطور إدراج الأمر البرمجي Application.Volatile True في مطلع الكود.

تقوم هذه التعليمة بتحويل الدالة المخصصة إلى “دالة متطايرة”، مما يجبر تطبيق إكسيل على إعادة تقييمها وتنفيذها مع كل عملية إعادة حساب تحدث في أي موضع عبر كافة مصنفات العمل المفتوحة. ورغم أن هذا يضمن دقة النتائج وتحديثها اللحظي، إلا أنه يمثل سلاحاً ذا حدين؛ فالإفراط في استخدام الدوال المتطايرة داخل ورقة عمل واحدة يؤدي إلى استنزاف هائل لطاقة المعالج وإبطاء وتيرة الإدخال اليدوي، نظراً لإعادة تشغيل الحلقات التكرارية للقاموس مع كل نقرة زر ينفذها المستخدم.

تقتضي الهندسة البرمجية المتوازنة تجنب جعل الدالة متطايرة إلا عند الضرورة القصوى، والاعتماد بدلاً من ذلك على التمرير الدقيق لكامل النطاقات المؤثرة كمعاملات رسمية للدالة (Arguments). فعندما يعلم إكسيل أن الخلية B1 تعتمد صراحة على النطاق A2:A100، فإنه سيتولى إعادة استدعاء الدالة تلقائياً فقط وحصراً عند حدوث تغيير حقيقي في إحدى خلايا هذا النطاق بالذات، محققاً التوازن الأمثل بين التفاعلية اللحظية والأداء الحسابي الرشيق.

12. التطبيقات العملية ودراسات الحالة في تحليل البيانات الواقعية

12.1 دراسة تطبيقية: حساب أسماء الفرق والكيانات الرياضية

لتجسيد هذه المفاهيم في واقع تطبيقي ملموس، نفترض وجود قاعدة بيانات واقعية لمباريات دوري كرة السلة للمحترفين، تتألف من عشرات الآلاف من السجلات التي تسجل الفرق المتنافسة في مواجهات الذهاب والإياب عبر مواسم متعددة. تتوزع أسماء الفرق في عمودين رئيسيين: “الفريق المضيف” في العمود A، و”الفريق الضيف” في العمود B. المطلوب من محلل البيانات هو حصر إجمالي الأندية والكيانات الرياضية الفريدة المشاركة في البطولة بصورة آلية ودقيقة، مع التغلب على تكرار ظهور الفريق الواحد عشرات المرات كضيف ومضيف، والتعامل مع الاختلافات البسيطة في المسافات والتنسيقات.

في هذا السيناريو، يتم توظيف كود VBA متقدم يقرأ العمودين معاً دفعة واحدة داخل مصفوفة ذاكرية، ويقوم بتطهير أسماء الفرق من الرموز والمسافات الزائدة، ثم يمرر الأسماء إلى كائن القاموس Scripting.Dictionary. وبفضل خاصية المفاتيح الفريدة، يتم تجاهل تكرار أسماء الفرق تلقائياً، سواء ظهر الفريق في عمود المضيف أو عمود الضيف، لنحصل في النهاية على رقم حاسم يمثل العدد الفعلي للأندية الفريدة المتنافسة.

ولا تقتصر قوة الحل البرمجي على إرجاع الرقم الإجمالي فحسب؛ بل يمكن تمديد الإجراء ليقوم بإنشاء ورقة عمل جديدة باسم “التقرير الإحصائي”، ويفرغ فيها القائمة الحصرية الكاملة لأسماء تلك الفرق الفريدة مرتبة أبجدياً باستخدام الدالة uniqueDict.Keys، مع إنشاء عمود مجاور يطبق دالة COUNTIF مخصصة لحساب التكرار النسبي لكل فريق، مما يحول الكود من مجرد عداد رقمي إلى نظام تحليلي متكامل لاستخلاص السمات وتلخيص البيانات المعقدة في ثوانٍ معدودة.

12.2 أفضل الممارسات المهنية في كتابة وإدارة أكواد VBA

إن الانتقال من مرحلة كتابة الأكواد العشوائية إلى الاحترافية المؤسسية يفرض الالتزام بمعايير الجودة العالمية في صيانة الأكواد البرمجية (Clean Code Architecture). يتصدر هذه المعايير التنسيق البصري البادئ (Indentation) الذي يبرز بوضوح حدود الحلقات التكرارية والكتل الشرطية، واستخدام تسميات ذات دلالة واضحة للمتغيرات (مثل uniqueCounter بدلاً من الرموز المبهمة كـ x و y)، وتوثيق الغرض المعماري من كل دالة بمقدمة توضيحية تشرح المدخلات والمخرجات المتوقعة.

وعلى صعيد النشر المؤسسي، يُعد تجميع هذه الدوال المخصصة داخل “إضافات إكسيل البرمجية” (Excel Add-ins – بصيغة .xlam) الخطوة الاستراتيجية الأكثر نضجاً. يتيح ذلك تثبيت الوظائف البرمجية مرة واحدة داخل نظام التشغيل، لتصبح متاحة بصورة دائمة لكافة موظفي المؤسسة عبر أي مصنف عمل جديد دون الحاجة لنسخ ولصق كود VBA داخل كل ملف على حدة، مما يضمن مركزية الصيانة وسهولة تحديث الخوارزميات متى تطلب الأمر.

أخيراً، تقتضي قواعد الحوكمة والأمان السيبراني تفعيل التوقيع الرقمي لوحدات الماكرو (Digital Signatures) باستخدام شهادات معتمدة، لحماية الأكواد المؤسسية من التعديل غير المصرح به وتفادي تحذيرات الأمان الصارمة التي تفرضها بيئات تشغيل ميكروسوفت الحديثة ضد ملفات الماكرو غير الموثوقة. إن تبني هذه الرؤية الشاملة يضمن أن تظل أدوات تحليل البيانات المبنية بلغة VBA حلولاً متينة، آمنة، وقابلة للتوسع ومواكبة متطلبات التحول الرقمي بكفاءة واقتدار.

الخاتمة

لقد أظهر هذا الدليل المرجعي الشامل أن مسألة حساب القيم الفريدة والمتميزة في بيئة إكسيل باستخدام لغة Visual Basic for Applications (VBA) ليست مجرد استدعاء لإجراء تكراري بسيط، بل هي منظومة هندسية متكاملة تتقاطع فيها خوارزميات التجزئة مع إدارة الذاكرة الصرفة وتطهير البيانات ومعالجة الاستثناءات الحسابية. ومن خلال المقارنة التحليلية المتعمقة، يتضح بجلاء تفوق كائن القاموس Scripting.Dictionary المدعوم بمصفوفات الذاكرة المؤقتة، بوصفه المعيار الذهبي الأكفأ والأسرع لمعالجة البيانات المؤسسية الضخمة، متجاوزاً القيود التشغيلية القاتلة للدوال التقليدية.

إن تمكن المطور والمحلل من تطويع هذه التقنيات البرمجية، وفهم الفروق الدقيقة بين حساسية الأحرف وحالات الفراغ ومعالجة الأخطاء، وتحويل الإجراءات إلى دوال وظيفية مخصصة (UDF) أو إضافات عامة (Add-ins)، يمنح المؤسسات ميزة تنافسية حاسمة في دقة التقارير وسرعة اتخاذ القرار. ومع استمرار تطور تقنيات البيانات، يظل التحكم في الشيفرة المصدرية على مستوى النواة الذاكرية هو السلاح الأقوى لبناء أدوات تحليلية رصينة وموثوقة تواجه تعقيدات البيانات الواقعية بأعلى درجات الكفاءة والاقتدار.

المراجع

تقييم هذا المحتوى

0.0 / 5 0 تقييمات

اقتباس هذا المقال

looti, M. (2026, سبتمبر 12). كيفية حساب القيم الفريدة في نطاق باستخدام VBA. عرب سايكلوجي. https://arabpsychology.com/statistics/how-to-count-unique-values-in-range-using-vba/
looti, Mohammed. “كيفية حساب القيم الفريدة في نطاق باستخدام VBA.” عرب سايكلوجي, 12 سبتمبر 2026, https://arabpsychology.com/statistics/how-to-count-unique-values-in-range-using-vba/.
looti, Mohammed. “كيفية حساب القيم الفريدة في نطاق باستخدام VBA.” عرب سايكلوجي. سبتمبر 12, 2026. https://arabpsychology.com/statistics/how-to-count-unique-values-in-range-using-vba/.