المجموعات في Kotlin: Set و Map ودوال المجموعات
في درس المصفوفات والقوائم تعرّفت على List و Map وعلى filter و map. الآن حان وقت إكمال الصورة: نوع ثالث مهم هو Set، وخرائط تتغير أثناء التشغيل، ومجموعة دوال تجعل ترتيب البيانات وتجميعها والبحث فيها سطراً واحداً. هذه الدوال هي ما يجعل كود Kotlin قصيراً وممتعاً في القراءة.
المجموعة Set
Section titled “المجموعة Set”المجموعة (Set) تشبه القائمة، لكنها ترفض التكرار:
val visitors = mutableSetOf("سارة", "عمر")println(visitors.add("ليلى"))println(visitors.add("سارة"))println(visitors)println("عمر" in visitors)
val numbers = listOf(3, 1, 3, 2, 1)println(numbers.toSet())println(numbers.distinct())truefalse[سارة, عمر, ليلى]true[3, 1, 2][3, 1, 2]setOf(...)للقراءة فقط، وmutableSetOf(...)تقبل الإضافة والحذف.addتُرجعfalseإذا كان العنصر موجوداً، ولا تضيفه مرة ثانية.toSet()تحوّل قائمة إلى مجموعة فتختفي التكرارات، وdistinct()تفعل الشيء نفسه وتُرجع قائمة.
المجموعة مثل سجل الحضور: الاسم يُكتب مرة واحدة مهما دخل صاحبه وخرج.
والمجموعات تدعم عمليات الرياضيات المعروفة:
val football = setOf("علي", "منى", "زياد")val swimming = setOf("منى", "هدى", "علي")
println(football intersect swimming)println(football union swimming)println(football subtract swimming)[علي, منى][علي, منى, زياد, هدى][زياد]intersect للمشترك بين المجموعتين، و union لكل العناصر، و subtract لما في الأولى وليس في الثانية.
الخريطة القابلة للتعديل MutableMap
Section titled “الخريطة القابلة للتعديل MutableMap”mapOf تنشئ خريطة للقراءة فقط. للإضافة والتعديل استخدم mutableMapOf:
val stock = mutableMapOf("قلم" to 40, "دفتر" to 15)stock["مسطرة"] = 8stock["قلم"] = 38stock.remove("دفتر")
println(stock)println(stock["قلم"])println(stock["ممحاة"])println(stock.getOrDefault("ممحاة", 0))println(stock.keys)println(stock.values.sum()){قلم=38, مسطرة=8}38null0[قلم, مسطرة]46stock[key] = value: تضيف زوجاً جديداً، أو تستبدل القيمة إذا كان المفتاح موجوداً. المفاتيح لا تتكرر.- قراءة مفتاح غير موجود تُرجع
null، ولهذا نوع القيمة المقروءةInt?. keysوvalues: كل المفاتيح وكل القيم.
والمرور على الخريطة يفكك كل زوج إلى متغيرين:
val ages = mapOf("علي" to 13, "سارة" to 14)for ((name, age) in ages) { println("$name عمره $age")}علي عمره 13سارة عمره 14مثال عملي: عدّ التكرارات
Section titled “مثال عملي: عدّ التكرارات”val votes = listOf("أحمر", "أزرق", "أحمر", "أخضر", "أحمر", "أزرق")val counts = mutableMapOf<String, Int>()for (vote in votes) { counts[vote] = (counts[vote] ?: 0) + 1}println(counts){أحمر=3, أزرق=2, أخضر=1}في كل مرة نقرأ العدد الحالي للون، أو صفراً بفضل المعامل ?: إذا لم يظهر من قبل، ثم نزيده واحداً.
الترتيب
Section titled “الترتيب”سنستخدم في بقية الدرس قائمة من كائنات data class:
data class Student(val name: String, val grade: Int, val city: String)
val students = listOf( Student("هدى", 88, "القاهرة"), Student("كريم", 95, "عمّان"), Student("ريم", 72, "القاهرة"), Student("زياد", 60, "تونس"),)println(students.sortedBy { it.grade }.map { it.name })println(students.sortedByDescending { it.grade }.map { it.name })println(students.maxByOrNull { it.grade }?.name)println(listOf(5, 2, 9).sorted())[زياد, ريم, هدى, كريم][كريم, هدى, ريم, زياد]كريم[2, 5, 9]sortedBy { ... }: ترتب تصاعدياً حسب الخاصية التي تختارها، وتُرجع قائمة جديدة دون المساس بالأصلية.maxByOrNull: العنصر صاحب أعلى قيمة، أوnullإذا كانت القائمة فارغة.
البحث والفحص
Section titled “البحث والفحص”println(students.find { it.grade > 90 }?.name)println(students.find { it.grade > 99 })println(students.any { it.city == "تونس" })println(students.all { it.grade >= 50 })println(students.count { it.city == "القاهرة" })println(students.sumOf { it.grade })كريمnulltruetrue2315| الدالة | السؤال الذي تجيب عنه |
|---|---|
find |
ما أول عنصر يحقق الشرط؟ (أو null) |
any |
هل يوجد عنصر واحد على الأقل يحققه؟ |
all |
هل كل العناصر تحققه؟ |
count |
كم عنصراً يحققه؟ |
التجميع groupBy
Section titled “التجميع groupBy”groupBy توزّع العناصر على مجموعات حسب مفتاح، وتُرجع خريطة:
val byCity = students.groupBy { it.city }for ((city, group) in byCity) { println("$city: ${group.map { it.name }}")}
val (passed, failed) = students.partition { it.grade >= 75 }println(passed.size)println(failed.size)القاهرة: [هدى, ريم]عمّان: [كريم]تونس: [زياد]22تخيّل معلماً يطلب من الطلاب أن يقفوا حسب مدنهم: كل مدينة تصبح مفتاحاً، وطلابها قائمة. أما partition فتقسم القائمة إلى قائمتين فقط: من حقق الشرط، ومن لم يحققه.
سلسلة العمليات
Section titled “سلسلة العمليات”القوة الحقيقية تظهر عند ربط الدوال ببعضها:
val report = students .filter { it.grade >= 70 } .sortedByDescending { it.grade } .joinToString(" ، ") { "${it.name} (${it.grade})" }println(report)كريم (95) ، هدى (88) ، ريم (72)تُقرأ السلسلة من الأعلى للأسفل كجملة: خذ الطلاب، اختر الناجحين، رتّبهم تنازلياً، ثم ادمجهم في نص.
جرّب بنفسك
Section titled “جرّب بنفسك”- أنشئ قائمة أرقام فيها تكرارات، واطبع عدد الأرقام المختلفة فيها.
- أنشئ
mutableMapOfتربط اسم المنتج بسعره، وأضف منتجاً، وعدّل سعر آخر، ثم اطبع أغلى منتج باستخدامmaxByOrNull. - أنشئ
data class Book(val title: String, val pages: Int, val author: String)وقائمة كتب، واطبع عناوين الكتب مرتبة حسب عدد الصفحات. - تحدٍّ: جمّع الكتب حسب المؤلف بـ
groupBy، واطبع لكل مؤلف مجموع صفحات كتبه.
أخطاء شائعة
Section titled “أخطاء شائعة”- التعديل على مجموعة للقراءة فقط:
listOfوsetOfوmapOfلا تملكaddولاremove. استخدم النسخ التي تبدأ بـmutable. - توقّع تعديل الأصل:
sortedByوfilterتُرجع قائمة جديدة. إذا لم تخزن النتيجة فلن يتغير شيء. - نسيان null مع الخرائط:
stock["قلم"] + 1خطأ ترجمة لأن القيمة قد تكونnull. استخدم?:أوgetOrDefault. - first بدل find:
first { ... }ترميNoSuchElementExceptionإذا لم تجد شيئاً.findوfirstOrNullأكثر أماناً.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما ناتج listOf(1, 2, 2, 3, 3, 3).toSet().size؟
المجموعة تحذف التكرارات، فتبقى العناصر 1 و 2 و 3 فقط.
سؤال
ماذا تُرجع الدالة groupBy؟
groupBy تُرجع Map مفاتيحها قيم التجميع، وقيمها قوائم العناصر التابعة لكل مفتاح.
الخلاصة
Section titled “الخلاصة”Setمجموعة بلا تكرار، وتدعمintersectوunionوsubtract.mutableMapOfخريطة تقبل الإضافة والتعديل والحذف، وقراءتها قد تُرجعnull.sortedByللترتيب، وfindوanyوallوcountللبحث والفحص.groupByتجمّع العناصر في خريطة، وpartitionتقسمها إلى قائمتين.- مبروك! أنهيت كورس Kotlin. الخطوة التالية: ابنِ أول تطبيق أندرويد لك.