ترتيب المجموعات حسب عدة حقول في Kotlin: 5 أخطاء قاتلة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا يكسر الترتيب "البسيط" في Kotlin الكود؟

\n

مرحباً يا زميل! إذا كنت قد كتبت يوماً ما في Kotlin وحاولت ترتيب قائمة المهام أولاً حسب التاريخ ثم حسب الأولوية، فبالتأكيد واجهت موقفاً تبدو فيه النتيجة وكأنها مجموعة عشوائية. لست وحدك — فقد حصد سؤال "Sort collection by multiple fields in Kotlin" آلاف المشاهدات على Stack Overflow. قد يبدو الأمر بسيطاً؟ لكن الشيطان، كما هو الحال دائماً، في التفاصيل.

\n

في هذه المقالة، سنحلل أهم الأخطاء عند الترتيب حسب عدة حقول في Kotlin التي يرتكبها حتى المطورون ذوو الخبرة. ستتعرف على كيفية عمل sortedWith، ولماذا compareBy و thenBy هما أفضل أصدقائك، وكيف لا تفقد البيانات بسبب null. هيا بنا!

\n\n

الخطأ رقم 1: الترتيب كما في Java — عبر Comparator يدوياً

\n

Kotlin ليس Java. لكن الكثيرين، القادمين من عالم Java، يكتبون:

\n
val list = listOf(\n    User("Alice", 25),\n    User("Bob", 20),\n    User("Charlie", 25)\n)\n\nval sorted = list.sortedWith(Comparator { a, b ->\n    val ageCompare = a.age.compareTo(b.age)\n    if (ageCompare != 0) ageCompare else a.name.compareTo(b.name)\n})\n
\n

نعم، هذا يعمل. لكنه غير قابل للقراءة بشكل فظيع، ويمكنك بسهولة أن تتوه في سلاسل if-else. يقدم Kotlin طريقة أنيقة — دوال المقارنة (Comparator functions).

\n

ما الطريقة الصحيحة؟

\n
val sorted = list.sortedWith(compareBy<User> { it.age }.thenBy { it.name })\n
\n

قصيرة، واضحة، بدون منطق زائد. compareBy تنشئ مقارناً أساسياً، و thenBy تضيف الحقول الثانوية. هذا هو الترتيب حسب عدة حقول في Kotlin في أفضل صوره.

\n\n

الخطأ رقم 2: تجاهل null عند الترتيب

\n

لنفترض أن لدينا قائمة مهام حيث deadline قد يكون null. إذا كتبت ببساطة:

\n
tasks.sortedBy { it.deadline }\n
\n

…ستحصل على NullPointerException. Kotlin صارم تجاه null، وهذا رائع. لكن ماذا لو كان null أمراً طبيعياً؟

\n

الحل: nullsFirst / nullsLast

\n

استخدم أغلفة خاصة:

\n
val sorted = tasks.sortedWith(\n    compareBy<Task>(nullsLast()) { it.deadline }\n        .thenBy { it.priority }\n)\n
\n

هنا، nullsLast() تضع جميع التواريخ null في نهاية القائمة. يوجد أيضاً nullsFirst() — والعكس صحيح. هذا يحل المشكلة، ويصبح ترتيب مجموعات Kotlin آمناً.

\n\n

الخطأ رقم 3: الخلط بين sortedBy و sortedWith

\n

يكتب العديد من المبتدئين:

\n
list.sortedBy { it.age }.sortedBy { it.name }\n
\n

ويتفاجؤون بأن الترتيب حسب الاسم "يلغي" الترتيب حسب العمر. sortedBy تنشئ قائمة جديدة في كل مرة وتُرتب حسب حقل واحد فقط. الاستدعاء الأخير هو المسيطر.

\n

كيف نصلح ذلك؟

\n

استخدم sortedWith مع سلسلة compareBy:

\n
list.sortedWith(compareBy({ it.age }, { it.name }))\n
\n

أو النسخة متعددة الأسطر للمنطق المعقد:

\n
list.sortedWith(compareBy<Person> { it.age }.thenByDescending { it.name })\n
\n

تذكر: استدعاء واحد لـ sortedWith — ترتيب واحد حسب عدة حقول.

\n\n

الخطأ رقم 4: الترتيب العكسي — في المكان الخطأ

\n

أحياناً نحتاج إلى ترتيب تصاعدي للعمر، لكن تنازلي للاسم. الخطأ النموذجي:

\n
list.sortedByDescending { it.age }.sortedBy { it.name }\n
\n

مرة أخرى نفس المشكلة — الترتيب الثاني يلغي الأول. الطريقة الصحيحة:

\n
val sorted = list.sortedWith(\n    compareBy<Person> { it.age }\n        .thenByDescending { it.name }\n)\n
\n

أو للتحكم الكامل:

\n
val sorted = list.sortedWith(\n    compareBy(Person::age).thenByDescending(Person::name)\n)\n<                

المدونات

توصيات الكتب