التجميع حسب عدة أعمدة في LINQ: أمثلة وأفضل الممارسات

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

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

ابدأ الدورة

كيفية تجميع البيانات حسب حقلين أو أكثر باستخدام LINQ في C#؟

\n\n

مرحباً يا صديقي! إذا سبق لك كتابة استعلامات لقواعد البيانات بلغة SQL، فبالتأكيد تتذكر عبارة GROUP BY مع عدة أعمدة. فهي تسمح لك بتقسيم البيانات إلى سلال منطقية - على سبيل المثال، تجميع المبيعات أولاً حسب المدينة، ثم داخلياً حسب فئة المنتج. في لغة C#، يتم استخدام GroupBy من LINQ لنفس الغرض. ولكن المشكلة: GroupBy القياسي يقبل مفتاحاً واحداً فقط. ماذا لو كنت بحاجة إلى التجميع حسب عدة أعمدة؟

\n\n

لا تقلق! الحل أنيق وبسيط - نقوم بإنشاء نوع مجهول يدمج الحقول المطلوبة في مفتاح مركب واحد. في هذه المقالة، سأعرض لك ليس فقط الصيغة الأساسية، ولكن أيضاً أمثلة حقيقية مع كود يمكنك تطبيقه فوراً في مشاريعك. هيا بنا!

\n\n

المشكلة: مفتاح واحد - هذا قليل جداً

\n\n

تخيل أن لديك قائمة بالطلاب:

\n\n
public class Student\n{\n    public string Name { get; set; }\n    public string Course { get; set; }\n    public int Grade { get; set; }\n}\n\nList<Student> students = new List<Student>\n{\n    new Student { Name = "آنا", Course = "رياضيات", Grade = 85 },\n    new Student { Name = "بوريس", Course = "رياضيات", Grade = 90 },\n    new Student { Name = "آنا", Course = "فيزياء", Grade = 78 },\n    new Student { Name = "بوريس", Course = "فيزياء", Grade = 82 },\n    new Student { Name = "آنا", Course = "رياضيات", Grade = 95 }\n};
\n\n

تريد معرفة متوسط درجة كل طالب في كل مقرر. إذا استخدمت GroupBy حسب الاسم فقط، فسيتم خلط بيانات آنا في الرياضيات والفيزياء. إذا استخدمته حسب المقرر فقط، فسيتم فقدان الارتباط بالطالب. أنت بحاجة إلى تجميع حسب عمودين: الاسم + المقرر.

\n\n

الحل: النوع المجهول كمفتاح مركب

\n\n

في لغة C#، يمكننا إنشاء كائن مؤقت بالحقول المطلوبة مباشرة في المكان:

\n\n
var grouped = students\n    .GroupBy(s => new { s.Name, s.Course })\n    .Select(g => new\n    {\n        g.Key.Name,\n        g.Key.Course,\n        AverageGrade = g.Average(st => st.Grade)\n    });\n\nforeach (var item in grouped)\n{\n    Console.WriteLine($"{item.Name} | {item.Course} | متوسط الدرجة: {item.AverageGrade:F2}");\n}
\n\n

النتيجة:
\nآنا | رياضيات | متوسط الدرجة: 90.00
\nبوريس | رياضيات | متوسط الدرجة: 90.00
\nآنا | فيزياء | متوسط الدرجة: 78.00
\nبوريس | فيزياء | متوسط الدرجة: 82.00

\n\n

لاحظ: new { s.Name, s.Course } هو النوع المجهول. يقوم LINQ تلقائياً بمقارنة الكائنات بناءً على جميع خصائصها (مساواة القيمة).

\n\n

العمل مع IGrouping والمجموعات المتداخلة

\n\n

أحياناً تحتاج ليس فقط إلى تجميع البيانات، بل إلى الاحتفاظ بجميع العناصر الأصلية داخل المجموعة. لهذا، يتم استخدام الواجهة IGrouping<TKey, TElement>. كل مجموعة تحتوي على مفتاح ومجموعة من العناصر:

\n\n
var groups = students.GroupBy(s => new { s.Name, s.Course });\n\nforeach (var group in groups)\n{\n    Console.WriteLine($"الطالب: {group.Key.Name}, المقرر: {group.Key.Course}");\n    foreach (var student in group)\n    {\n        Console.WriteLine($"  الدرجة: {student.Grade}");\n    }\n}
\n\n

هذا الأسلوب مثالي عندما تحتاج، على سبيل المثال، إلى إنشاء تقرير مفصل.

\n\n

صيغة الاستعلام (query) مقابل الصيغة التسلسلية (fluent) - أيهما تختار؟

\n\n

يدعم LINQ أسلوبين لكتابة الاستعلامات. دعنا نرى كليهما:

\n\n

1. الصيغة التسلسلية (Fluent)

\n
var result = students\n    .GroupBy(s => new { s.Name, s.Course })\n    .Select(g => new { g.Key.Name, g.Key.Course, Count = g.Count() });
\n\n

2. صيغة الاستعلام (Query)

\n
var result = from student in students\n             group student by new { student.Name, stud                

المدونات

توصيات الكتب