# LINQ, חלק א' — Where, Select, OrderBy📖 שיעור

## מה זה?

כדי לסנן את התלמידים הבוגרים מ-`List<Student>`, הדרך "הידנית" היא לולאת `foreach`, `if` בפנים, ורשימה חדשה שממלאים ידנית — 5-6 שורות לפעולה פשוטה. LINQ (Language Integrated Query) פותר את זה בשורה אחת: `students.Where(s => s.Age >= 18)`. זו לא רק תמציתיות — זו שפה משותפת וקריאה לפעולות שחוזרות שוב ושוב על כל אוסף: סינון, מיפוי, מיון.

## מילות מפתח שחשוב לזכור

• LINQ — סט מתודות מובנות על כל `IEnumerable<T>` (כולל `List<T>`, מערכים) לסינון, מיפוי ומיון

• Lambda Expression (`s => s.Age >= 18`) — פונקציה אנונימית קצרה: "עבור כל `s`, החזר את..."; הדרך הנפוצה להעביר לוגיקה ל-LINQ

• `Where` — מסנן אוסף, מחזיר רק איברים שעונים על תנאי

• `Select` — ממפה כל איבר לערך אחר (טרנספורמציה), לא בהכרח מאותו טיפוס

• `OrderBy` / `OrderByDescending` — ממיין אוסף לפי מפתח, בלי לשנות את המקור

```csharp
List<Student> students = new List<Student>
{
    new Student("Dana", 25), new Student("Avi", 17), new Student("Rotem", 30)
};

// Before LINQ — manual
List<Student> adultsManual = new List<Student>();
foreach (var s in students)
{
    if (s.Age >= 18) adultsManual.Add(s);
}

// With LINQ — one line
List<Student> adults = students.Where(s => s.Age >= 18).ToList();
```

```csharp
// Select — maps to another type (here: from Student to string)
List<string> names = students.Select(s => s.Name).ToList();
// { "Dana", "Avi", "Rotem" }

// Combining Where + Select — filter, then map, method chaining
List<string> adultNames = students.Where(s => s.Age >= 18).Select(s => s.Name).ToList();
```

```csharp
// OrderBy / OrderByDescending — sorting without changing the source
List<Student> byAgeAsc = students.OrderBy(s => s.Age).ToList();
List<Student> byAgeDesc = students.OrderByDescending(s => s.Age).ToList();
Console.WriteLine(students.Count);   // still the original list, in the original order — OrderBy doesn't change it
```

## הסבר עיקרי

Lambda Expression היא "פונקציה בשורה אחת" — `s => s.Age >= 18` שקול לפונקציה `bool IsAdult(Student s) { return s.Age >= 18; }`, רק בלי להגדיר אותה בנפרד עם שם. ה-`s` משמאל לחץ הוא הפרמטר (השם שרירותי — יכול להיות כל אות/מילה), הביטוי מימין הוא מה שמוחזר. LINQ בנוי כולו על העברת לוגיקה קטנה כזו כארגומנט למתודות מובנות.

`Where` מול `Select` — `Where` **מסנן** (מחזיר תת-קבוצה מאותו טיפוס, פחות איברים); `Select` **ממפה** (מחזיר את אותו מספר איברים, אך אולי בטיפוס אחר לגמרי — `Student` → `string`). שרשור שניהם (`Where(...).Select(...)`) הוא דפוס נפוץ מאוד: קודם לסנן למה שרלוונטי, ואז לחלץ רק מה שצריך מכל איבר.

LINQ לא משנה את המקור — `OrderBy` (וכל שאר מתודות LINQ) **תמיד** מחזירות **אוסף חדש** — הרשימה המקורית (`students`) נשארת בדיוק כמו שהייתה. זה שונה מ-`.Sort()` (מהיחידה הקודמת, על `IComparable`) שכן משנה את הרשימה **במקום**. ההבדל הזה חשוב: LINQ בטוח יותר כי אי אפשר "לשבור" בטעות רשימה שקוד אחר עדיין מסתמך על הסדר המקורי שלה.

## יתרונות

LINQ הופך פעולות סינון/מיפוי/מיון נפוצות לשורה אחת קריאה, במקום 5-6 שורות `foreach` ידניות; שרשור מתודות (`Where(...).Select(...)`) מבטא זרימת עיבוד נתונים בבירור; לא משנה את האוסף המקורי — בטוח יותר מ-`.Sort()`.

## חסרונות

שרשור ארוך מדי (5+ מתודות ברצף) יכול להיות קשה לקריאה כמו קוד ידני מסורבל; LINQ מריץ את הביטוי על **כל** איבר בכל קריאה — לולאה ידנית ממוטבת-יד עשויה להיות מעט מהירה יותר בתרחישי ביצועים קריטיים מאוד.

## נקודות חשובות

• Lambda (`s => expr`) היא פונקציה אנונימית קצרה — הדרך הנפוצה להעביר לוגיקה ל-LINQ

• `Where` מסנן (פחות איברים, אותו טיפוס); `Select` ממפה (אותו מספר איברים, טיפוס אפשרי אחר)

• LINQ תמיד מחזיר אוסף **חדש** — לא משנה את המקור, בניגוד ל-`.Sort()`

• `.ToList()` בסוף שרשור LINQ ממיר את התוצאה (שהיא `IEnumerable<T>`) בחזרה ל-`List<T>` קונקרטית

## טעויות נפוצות

• לשכוח `.ToList()` בסוף ולנסות להשתמש בתוצאה כאילו היא `List<T>` ישירות (זה `IEnumerable<T>` עד שממירים)

• לצפות ש-`OrderBy` ישנה את הרשימה המקורית — הוא לא, מחזיר עותק ממוין חדש

• לכתוב שרשור LINQ ארוך מדי ובלתי-קריא במקום לפרק לצעדים עם שמות ברורים

## סיכום

LINQ הופך פעולות אוסף נפוצות (סינון, מיפוי, מיון) לשורה אחת קריאה במקום לולאות `foreach` ידניות. Lambda Expressions (`s => expr`) הן הדרך להעביר לוגיקה קצרה ל-`Where`/`Select`/`OrderBy`. חשוב לזכור: LINQ תמיד מחזיר אוסף חדש, לא משנה את המקור, ו-`.ToList()` נדרש כדי להמיר תוצאה בחזרה ל-`List<T>` קונקרטית לשימוש נוסף.

## דוקומנטציה רשמית

[Microsoft Learn — LINQ overview](https://learn.microsoft.com/dotnet/csharp/linq/)

---

## תרגילים

### תרגיל 1 — Where

**המשימה:** מתוך `List<int>` עם 10 מספרים, סננו רק את הזוגיים עם `Where` והדפיסו את התוצאה.

**בדיקה:** רק המספרים הזוגיים מהרשימה המקורית מופיעים בתוצאה.

### תרגיל 2 — Select ושרשור

**המשימה:** מתוך `List<Student>`, הפיקו `List<string>` עם משפט תיאור לכל תלמיד (`"דנה, גיל 25"`) בעזרת `Select`, אך רק עבור תלמידים בגילאי 18+ (שילוב עם `Where`).

**בדיקה:** הרשימה המתקבלת מכילה רק תלמידים בגירים, כל אחד כמחרוזת מתוארת נכון.

---

## פרויקט מסכם

**המשימה:** בנו "לוח תוצאות" למבחן — מתוך `List<Student>` עם ציון (`Score`), הפיקו דוח ממוין.

**דרישות:**
1. סננו עם `Where` רק תלמידים שעברו (`Score >= 60`)
2. מיינו את התוצאה עם `OrderByDescending` לפי ציון — הגבוה ביותר ראשון
3. הפיקו עם `Select` רשימת מחרוזות בפורמט `"1. דנה — 95"` (כולל מיקום בדירוג), והדפיסו את כולן

**בדיקה:** רק תלמידים שעברו מופיעים; הרשימה ממוינת מהציון הגבוה לנמוך; המיקום בדירוג (1, 2, 3...) נכון עבור כל תלמיד.

## מה בפרק הבא

בפרק הבא — **LINQ, חלק ב'**: `GroupBy` לקיבוץ נתונים לפי מפתח, ו-`FirstOrDefault` לשליפה בטוחה של איבר בודד — הכלים האחרונים לקראת הפרויקט המסכם.
