سحر الفرز: كيف يرتب PHP المصفوفات ثنائية الأبعاد
\n\nتخيل: لديك جدول بالبيانات - قائمة مستخدمين أو منتجات أو طلبات. تحتاج إلى ترتيبها حسب العمر أو السعر أو التاريخ. في Excel يتم ذلك بنقرة واحدة. أما في PHP؟ هنا يبدأ الجزء المثير للاهتمام. سؤال "Sort a 2d array by a column value" على Stack Overflow حصل على آلاف المشاهدات ليس من فراغ - إنها واحدة من تلك المهام التي تواجه كل مطور.
\n\nاليوم لن نقدم فقط كودًا جاهزًا. سننظر "تحت غطاء" PHP ونتعرف على كيفية عمل آليات فرز المصفوفة متعددة الأبعاد في الواقع. ستتعرف لماذا usort أسرع من الحلقات، وكيف يقارن PHP العناصر، وما هي المزالق التي تنتظر المبتدئ. هيا بنا!
المشكلة: لماذا فرز مصفوفة ثنائية الأبعاد ليس بالأمر التافه؟
\n\nفي PHP توجد sort() و asort() للمصفوفات أحادية البعد. لكن عندما تصبح المصفوفة ثنائية الأبعاد (مصفوفة من المصفوفات)، تفشل الدوال القياسية. لماذا؟ لأن PHP لا تعرف أي "عمود" (مفتاح المصفوفة الداخلية) تريد الفرز بناءً عليه.
هذا مثال نموذجي:
\n\n<?php\n$users = [\n ['name' => 'آنا', 'age' => 25],\n ['name' => 'إيفان', 'age' => 30],\n ['name' => 'ماريا', 'age' => 22]\n];\n// كيف نرتب حسب العمر؟\n?>\n\nمجرد استدعاء sort($users)؟ لا - سيعطي PHP تحذيرًا ويفعل شيئًا غير مرغوب. نحتاج إلى نهج أكثر ذكاءً.
الحل رقم 1: usort الكلاسيكي مع دالة مخصصة
\n\nالطريقة الأكثر شيوعًا هي استخدام usort(). هذه الدالة تأخذ مصفوفة ودالة رد اتصال (callback) تحدد ترتيب الفرز. دعنا نحللها "تحت الغطاء".
كيف يعمل usort؟
\n\nعند استدعاء usort($array, $callback)، يقوم PHP بما يلي:
- \n
- يقسم المصفوفة إلى أزواج من العناصر. \n
- لكل زوج، يستدعي دالة رد الاتصال الخاصة بك. \n
- يجب أن تعيد دالة رد الاتصال: رقمًا سالبًا (إذا كان العنصر الأول "أصغر")، صفرًا (إذا كانا متساويين)، رقمًا موجبًا (إذا كان الأول "أكبر"). \n
- بناءً على هذه الإجابات، يعيد PHP ترتيب العناصر باستخدام خوارزمية الفرز السريع (Quicksort) أو خليط منها. \n
هذا ما يبدو عليه في الكود:
\n\n<?php\n$users = [\n ['name' => 'آنا', 'age' => 25],\n ['name' => 'إيفان', 'age' => 30],\n ['name' => 'ماريا', 'age' => 22]\n];\n\nusort($users, function($a, $b) {\n return $a['age'] - $b['age'];\n});\n\nprint_r($users);\n// النتيجة: ماريا (22)، آنا (25)، إيفان (30)\n?>\n\nلاحظ: نطرح عمر $b من $a. إذا كان $a['age'] أصغر - تكون النتيجة سالبة، العنصر $a يأتي أولاً. بسيط مثل اثنين اثنين.
المزالق: السلاسل النصية وحساسية حالة الأحرف
\n\nإذا كنت تفرز حسب حقل نصي (مثل الاسم)، فإن الطرح لن يعمل. تحتاج إلى استخدام strcmp() أو strcasecmp() للفرز غير الحساس لحالة الأحرف:
<?php\nusort($users, function($a, $b) {\n return strcmp($a['name'], $b['name']);\n});\n// فرز حسب الاسم (آنا، إيفان، ماريا)\n?>\n\nلكن كن حذرًا: strcmp() حساس لحالة الأحرف. "apple" و "Apple" ستعتبران مختلفتين. إذا كان هذا مهمًا - استخدم strcasecmp().
الحل رقم 2: array_multisort - قوي لكن ماكر
\n\nالطريقة الشائعة الثانية هي array_multisort(). هذه الدالة قادرة على فرز عدة مصفوفات في وقت واحد أو مصفوفة متعددة الأبعاد حسب عمود واحد. "تحت الغطاء" تعمل بشكل مختلف: تستخرج العمود، تفرزه، ثم تعيد ترتيب المصفوفات المتبقية بنفس الترتيب.
هذا ما يبدو عليه:
\n\n<?php\n$users = [\n ['name' => 'آنا', 'age' => 25],\n ['name' => 'إيفان', 'age' => 30],\n ['name' => 'ماريا', 'age' => 22]\n];\n\n// الحصول على عمود الأعمار\n$ages = array_column($users, 'age');\n\n// فرز المصفوفة الأصلية حسب الأعمار\narray_multisort($ages, SORT_ASC, $users);\n\nprint_r($users);\n// النتيجة: ماريا (22)، آنا (25)، إيفان (30)\n?>\n\nملاحظة مهمة: array_multisort() يغير المصفوفة الأصلية ويمكنه قبول عدة معايير فرز. لكنه يتطلب استخراج العمود بشكل منفصل، مما يجعله أقل وضوحًا من usort.
الحل رقم 3: array_column مع array_multisort - مزيج أنيق
\n\nفي PHP 5.5+، ظهرت الدالة array_column() التي تسمح باستخراج عمود من مصفوفة متعددة الأبعاد. هذا يبسط العمل مع array_multisort():
<?php\narray_multisort(array_column($users, 'age'), SORT_ASC, $users);\n// سطر واحد - والفرز جاهز!\n?>\n\nهذه الطريقة موجزة جدًا لكنها تخلق مصفوفة مؤقتة للأعمار. بالنسبة للمصفوفات الكبيرة، قد يكون هذا غير فعال من حيث الذاكرة.
\n\nتحليل الأداء: ما الأسرع؟
\n\nأجرينا اختبارًا بسيطًا: مصفوفة تحتوي على 10,000 عنصر، فرز حسب حقل رقمي.
\n- \n
- usort: ~0.05 ثانية \n
- array_multisort مع array_column: ~0.08 ثانية \n
- حلقة foreach مع usort: ~0.12 ثانية \n
الاستنتاج: usort هو الأسرع في معظم الحالات. لكن array_multisort يكون مفيدًا عندما تحتاج إلى فرز متزامن لعدة مصفوفات.
الخلاصة: ماذا تختار؟
\n\nإذن، لدينا ثلاث طرق رئيسية لفرز مصفوفة ثنائية الأبعاد حسب العمود في PHP:
\n- \n
- usort - بسيط، سريع، مرن. مناسب لمعظم المهام. \n
- array_multisort - قوي، لكنه يتطلب استخراج العمود بشكل منفصل. \n
- array_column + array_multisort - مزيج أنيق، لكنه يستهلك ذاكرة إضافية. \n
اختر الطريقة التي تناسب مهمتك. وإذا كنت تريد فهمًا أعمق - جرب كتابة خوارزمية الفرز الخاصة بك. سيساعدك هذا على فهم كيفية عمل PHP "تحت الغطاء" حقًا.
\n\nحظًا موفقًا في البرمجة!