Group by في LINQ: فهم التجميع بأمثلة عملية
\n\nإذا كنت قد كتبت استعلامات على المجموعات في C# ولو مرة واحدة، فبالتأكيد واجهت مهمة: "كيف يمكنني تجميع البيانات؟". موضوع group by in LINQ هو من أكثر المواضيع شيوعًا على Stack Overflow (عشرات الآلاف من المشاهدات). وهذا ليس من قبيل الصدفة: يبدو التجميع بسيطًا فقط للوهلة الأولى. في الواقع، يتعثر المبتدئون في المزالق النموذجية: كيفية اختيار أول سجل في المجموعة، وكيفية حساب المجموع، أو إجراء تجميع متداخل.
\n\nفي هذه المقالة، سنشرح أكثر 5 أسئلة شيوعًا حول LINQ GroupBy ونقدم إجابات عملية. فلنبدأ!
\n\n1. كيف يعمل GroupBy في LINQ؟ الصيغة الأساسية
\n\nطريقة GroupBy هي نظير لعامل GROUP BY في SQL. تقوم بتقسيم التسلسل إلى مجموعات بناءً على مفتاح محدد. النتيجة هي مجموعة من كائنات IGrouping<TKey, TElement>، حيث يحتوي كل كائن على المفتاح وجميع العناصر التي تقع في هذه المجموعة.
إليك مثال بسيط. لنفترض أن لدينا قائمة بالطلاب:
\n\npublic class Student\n{\n public string Name { get; set; }\n public string Group { get; set; }\n public int Score { get; set; }\n}\n\nvar students = new List<Student>\n{\n new Student { Name = "آنا", Group = "A", Score = 85 },\n new Student { Name = "بوريس", Group = "B", Score = 90 },\n new Student { Name = "فيكا", Group = "A", Score = 78 },\n new Student { Name = "جليب", Group = "B", Score = 92 }\n};\n\nنقوم بالتجميع حسب اسم المجموعة:
\n\nvar grouped = students.GroupBy(s => s.Group);\n\nforeach (var group in grouped)\n{\n Console.WriteLine($"المجموعة: {group.Key}");\n foreach (var student in group)\n {\n Console.WriteLine($" {student.Name} — {student.Score}");\n }\n}\n\nالنتيجة:
\nالمجموعة: A\n آنا — 85\n فيكا — 78\nالمجموعة: B\n بوريس — 90\n جليب — 92\n\nالأمر بسيط. لكن الشيطان، كما هو الحال دائمًا، يكمن في التفاصيل.
\n\n2. كيف تختار أول سجل من كل مجموعة؟
\n\nهذا هو السؤال الأكثر شيوعًا حول group by in LINQ. لنفترض أننا بحاجة للحصول على أفضل طالب من كل مجموعة (بأعلى درجة). كيف نفعل ذلك؟
\n\nالحل هو الجمع بين GroupBy و First() أو OrderByDescending:
var bestStudents = students\n .GroupBy(s => s.Group)\n .Select(g => g.OrderByDescending(s => s.Score).First());\n\nforeach (var student in bestStudents)\n{\n Console.WriteLine($"{student.Name} — {student.Group} — {student.Score}");\n}\n\nالنتيجة: آنا (85) وجليب (92). لاحظ: داخل Select نقوم بترتيب المجموعة ونأخذ العنصر الأول. هذه هي إجابة سؤال "كيفية عمل LINQ group by first".
هام: First() سيؤدي إلى استثناء إذا كانت المجموعة فارغة. إذا كان ذلك ممكنًا، استخدم FirstOrDefault().
3. كيف تحسب المجموع، العدد، أو المتوسط لكل مجموعة؟
\n\nغالبًا بعد التجميع، لا نحتاج فقط إلى تكرار العناصر، بل إلى الحصول على قيم مجمعة. على سبيل المثال، مجموع الدرجات لكل مجموعة أو متوسط الدرجات.
\n\nنستخدم Sum و Count و Average:
var groupStats = students\n .GroupBy(s => s.Group)\n .Select(g => new\n {\n Group = g.Key,\n Count = g.Count(),\n TotalScore = g.Sum(s => s.Score),\n AverageScore = g.Average(s => s.Score)\n });\n\nforeach (var stat in groupStats)\n{\n Console.WriteLine($"المجموعة {stat.Group}: {stat.Count} طلاب، المجموع {stat.TotalScore}، المتوسط {stat.AverageScore:F2}");\n}\n\nالنتيجة:
\nالمجموعة A: 2 طلاب، المجموع 163، المتوسط 81.50\nالمجموعة B: 2 طلاب، المجموع 182، المتوسط 91.00\n\nيمكنك أيضًا استخدام Min و Max — في