استخراج مفاتيح map في Go: دليل شامل 2025

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

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

ابدأ الدورة

المشكلة الأزلية لمطوري Go: كيف تستخرج جميع المفاتيح من map بسرعة؟

\n

هل سبق لك أن كتبت كودًا كهذا في Go؟

\n
m := map[string]int{"go": 1, "python": 2, "rust": 3}\nkeys := make([]string, 0, len(m))\nfor k := range m {\n    keys = append(keys, k)\n}\n
\n

إذا كان جوابك نعم، فأنت لست وحدك. استخراج قائمة المفاتيح من map هي واحدة من أكثر الأسئلة شيوعًا على Stack Overflow في قسم Go. يحصد السؤال آلاف المشاهدات، وتحته عشرات الإجابات بطرق مختلفة. وهذا ليس مستغربًا: فـ Go لا تحتوي على دالة مدمجة keys() مثل Python أو JavaScript. في كل مرة تضطر لإعادة اختراع العجلة.

\n

لكن عام 2025 جاء بالتغييرات. مع إصدار Go 1.22 و Generics، بالإضافة إلى الأنماط الجديدة، ظهرت حلول أكثر أناقة. في هذا المقال، سنستعرض جميع طرق استخراج slice من مفاتيح map — من الطرق التقليدية إلى أفضل الممارسات الحديثة. هيا بنا!

\n\n

لماذا لا توجد دالة keys() مدمجة في Go؟

\n

قبل أن نغوص في الكود، دعنا نفهم فلسفة اللغة. Go لغة بسيطة. مبتكروها تعمدوا عدم إضافة دوال يمكن تنفيذها بسطر واحد. for k := range m هو الطريقة المدمجة للتكرار على map. لكن المشكلة تكمن في:

\n
    \n
  • ترتيب المفاتيح غير مضمون (وهذه ميزة وليس عيبًا)
  • \n
  • لا يوجد سكر نحوي لـ "اجمع كل شيء في slice"
  • \n
  • Generics ظهرت فقط في الإصدار 1.18، واستقرت في 1.22
  • \n
\n

لهذا السبب، طور المجتمع عدة أنماط. دعنا نقارن بينها.

\n\n

الطريقة 1: الحلقة التقليدية (المستوى الأساسي)

\n

أبسط وأوضح طريقة — هي تلك التي عرضناها في البداية. تعمل في أي إصدار من Go.

\n
func Keys(m map[string]int) []string {\n    keys := make([]string, 0, len(m))\n    for k := range m {\n        keys = append(keys, k)\n    }\n    return keys\n}\n
\n

المزايا:

\n
    \n
  • الشفافية — أي مبتدئ سيفهم ما يحدث
  • \n
  • لا تعتمد على إصدار Go
  • \n
  • يمكن تعديلها بسهولة (مثل إضافة فلترة)
  • \n
\n

العيوب:

\n
    \n
  • الكثير من الكود المتكرر لكل map
  • \n
  • يجب كتابة دالة منفصلة لكل نوع
  • \n
\n

الحكم: ممتاز للأمثلة التعليمية أو المهام الفردية. لكن في كود الإنتاج، نريد حلاً عامًا.

\n\n

الطريقة 2: Generics (Go 1.18+)

\n

مع ظهور Generics، أصبح من الممكن كتابة دالة واحدة لأي نوع من المفاتيح.

\n
func Keys[K comparable, V any](m map[K]V) []K {\n    keys := make([]K, 0, len(m))\n    for k := range m {\n        keys = append(keys, k)\n    }\n    return keys\n}\n\n// الاستخدام:\nm := map[string]int{"a": 1, "b": 2}\nkeys := Keys(m) // []string{"a", "b"}\n
\n> جميل، أليس كذلك؟ دالة واحدة وكل شيء يعمل. Go 1.22 جعل Generics أكثر استقرارًا، لذا هذه الطريقة هي الموصى بها للمشاريع الجديدة.

\n

هام: يجب أن تكون المفاتيح من النوع comparable. وهذا ينطبق تلقائيًا على جميع الأنواع القياسية (string, int, float, struct بدون slices، إلخ).

\n\n

الطريقة 3: استخدام reflect (للحالات الاستثنائية)

\n

أحيانًا تحتاج للتعامل مع map لا يُعرف نوعها إلا في وقت التشغيل. هنا يأتي دور حزمة reflect.

\n
func KeysReflect(m interface{}) []interface{} {\n    v := reflect.ValueOf(m)\n    if v.Kind() != reflect.Map {\n        return nil\n    }\n    keys := make([]interface{}, v.Len())\n    for i, k := range v.MapKeys() {\n        keys[i] = k.Interface()\n    }\n    return keys\n}\n
\n

متى تستخدم:

\n
    \n
  • عند تسلسل/إلغاء تسلسل هياكل غير معروفة
  • \n
  • في المكتبات العامة
  • \n
  • عندما يأتي نوع map من مصدر خارجي (JSON, YAML)
  • \n

المدونات

توصيات الكتب