Linq quick start โ
Dependencies used
This article uses Dumpify to display objects on console.
Filtering โ
Where โ
IEnumerable<int> items = [1, 2, 3, 4, 5, 6];
items.Where(x => x > 3).Dump();2
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ WhereEnumerableIterator<int> โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 4 โ
โ 5 โ
โ 6 โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ2
3
4
5
6
7
OfType โ
IEnumerable<object> items = [1, 2, 3, "4", 5, "6"];
items.OfType<string>().Dump();2
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ <OfTypeIterator>d__66<string> โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ "4" โ
โ "6" โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ2
3
4
5
6
INFO
OfType<T>() offers type info for compiler to determine the element type, however Where does not.
Partitioning โ
Skip โ
SkipLast โ
Take โ
TakeLast โ
SkipWhile โ
TakeWhile โ
Projection โ
Select โ
Generates elements from source.
IEnumerable<int> items = [1, 2, 3];
items.Select(x => x * 2).Dump();2
Select with index โ
Select has an overload to contain index of the element.
IEnumerable<char> items = ['a', 'b', 'c'];
items.Select((x, index) => $"{index}: {x}").Dump();
foreach (var (value, index) in items.Select((x, i) => (x, i)))
{
// ...
}2
3
4
5
6
SelectMany โ
SelectMany simply flatten two levels of collection into one that with generated elements.
IEnumerable<IEnumerable<int>> items = [[1, 2, 3], [4, 5, 6]];
items.SelectMany(x => x).Dump();2
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ SelectManySingleSelectorIterator<IEnumerable<int>, char> โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ '<' โ
โ '>' โ
โ 'z' โ
โ '_' โ
โ '_' โ
โ 'R' โ
โ 'e' โ
โ 'a' โ
โ 'd' โ
โ 'O' โ
โ 'n' โ
โ 'l' โ
โ 'y' โ
โ 'A' โ
โ 'r' โ
โ 'r' โ
โ 'a' โ
โ 'y' โ
โ '`' โ
โ '1' โ
โ '[' โ
โ 'S' โ
โ 'y' โ
โ 's' โ
โ 't' โ
โ 'e' โ
โ 'm' โ
โ '.' โ
โ 'I' โ
โ 'n' โ
โ 't' โ
โ '3' โ
โ '2' โ
โ ']' โ
โ '<' โ
โ '>' โ
โ 'z' โ
โ '_' โ
โ '_' โ
โ 'R' โ
โ 'e' โ
โ 'a' โ
โ 'd' โ
โ 'O' โ
โ 'n' โ
โ 'l' โ
โ 'y' โ
โ 'A' โ
โ 'r' โ
โ 'r' โ
โ 'a' โ
โ 'y' โ
โ '`' โ
โ '1' โ
โ '[' โ
โ 'S' โ
โ 'y' โ
โ 's' โ
โ 't' โ
โ 'e' โ
โ 'm' โ
โ '.' โ
โ 'I' โ
โ 'n' โ
โ 't' โ
โ '3' โ
โ '2' โ
โ ']' โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
The selected x is the one to be flattened. That's why we get a char sequence represents the concrete type name of x(<>z__ReadOnlyArray`1[System.Int32]< >z__ReadOnlyArray`1[System.Int32]).
IEnumerable<IEnumerable<int>> items = [[1, 2, 3], [4, 5, 6]];
items.SelectMany(x => x.ToString()).Dump();2
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ SelectManySingleSelectorIterator<IEnumerable<int>, int> โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 1 โ
โ 2 โ
โ 3 โ
โ 4 โ
โ 5 โ
โ 6 โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ2
3
4
5
6
7
8
9
10
SelectMany with index โ
IEnumerable<IEnumerable<int>> items = [[1, 2, 3], [4, 5, 6]];
items.SelectMany((x, i) => x.Select(x => $"{i}: {x}")).Dump();2
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ <SelectManyIterator>d__232<IEnumerable<int>, string> โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ "0: 1" โ
โ "0: 2" โ
โ "0: 3" โ
โ "1: 4" โ
โ "1: 5" โ
โ "1: 6" โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ2
3
4
5
6
7
8
9
10
Index โ
INFO
Coming from .NET 9...
Cast โ
Similar to OfType, Cast provides type info for compiler instead of using Select to cast objects, but it throws InvalidCastException if the element is not of the target type.
IEnumerable<int> items = [1, 2, 3];
items.Cast<string>(); // error2
Chunk โ
Chunk is opposite to SelectMany, it groups elements into chunks with size.
IEnumerable<int> items = [1, 2, 3, 4, 5, 6];
items.Chunk(5).Dump();2
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ <ChunkIterator>d__70<int> โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โญโโโโฌโโโโโโโโโฎ โ
โ โ # โ int[5] โ โ
โ โโโโโผโโโโโโโโโค โ
โ โ 0 โ 1 โ โ
โ โ 1 โ 2 โ โ
โ โ 2 โ 3 โ โ
โ โ 3 โ 4 โ โ
โ โ 4 โ 5 โ โ
โ โฐโโโโดโโโโโโโโโฏ โ
โ โญโโโโฌโโโโโโโโโฎ โ
โ โ # โ int[1] โ โ
โ โโโโโผโโโโโโโโโค โ
โ โ 0 โ 6 โ โ
โ โฐโโโโดโโโโโโโโโฏ โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Conditional Check โ
Any immediate execution โ
All immediate execution โ
Contains immediate execution โ
Manipulation โ
Append โ
Prepend โ
Aggregation โ
Count immediate execution โ
IEnumerable<int> items = [1, 2, 3, 4, 5, 6];
items.Count().Dump(); // 6
items.Count(x => x > 3).Dump(); // 32
3
CountBy immediate execution โ
coming from .net9 ...
LongCount immediate execution โ
TryGetNonEnumeratedCount immediate execution โ
IEnumerable<int> items = [1, 2, 3, 4, 5, 6];
items.TryGetNonEnumeratedCount(out var count1).Dump(); // true
items.Where(x => x > 3).TryGetNonEnumeratedCount(out var count2).Dump(); // false2
3
Sum immediate execution โ
Average immediate execution โ
Aggregate immediate execution โ
Aggregate is a very special extension to perform iteration from previous context to next context. It has three overloads:
- First overload
IEnumerable<int> items = [1, 2, 3, 4, 5, 6];
items.Aggregate((sum, current) => sum + current).Dump();
// equivalents to sum:
items.Sum().Dump();2
3
4
To perform concatenation to generate a cvs row or something like that:
IEnumerable<int> items = [1, 2, 3, 4, 5, 6];
items.Select(x => x.ToString()).Aggregate((context, current) => $"{context}, {current}").Dump();
// "1, 2, 3, 4, 5, 6"
IEnumerable<char> chars = ['a', 'b', 'c'];
chars.Select(x => x.ToString()).Aggregate((context, current) => $"{context}, {current}").Dump();
// "a, b, c"2
3
4
5
6
- Second overload
The second overload of Aggregate takes an initial value.
IEnumerable<int> items = [1, 2, 3, 4, 5, 6];
items.Aggregate(10, (sum, current) => sum + current).Dump(); // 312
To generate csv rows with header:
IEnumerable<Person> people = [new("John", 30), new("Jane", 20), new("Modi", 18)];
people.Select(x => $"{x.Name}, {x.Age}")
.Aggregate(
$"Name, Age,{Environment.NewLine}",
(context, current) => $"{context.TrimEnd(',')} {current}{Environment.NewLine}"
)
.Dump();
// "Name, Age,
// John, 30
// Jane, 20
// Modi, 18
// "
record class Person(string Name, int Age);2
3
4
5
6
7
8
9
10
11
12
13
- Third overload
The third overload of Aggregate takes an initial value and a result selector to perform post operations. The result selector takes the final result from iteration.
IEnumerable<int> items = [1, 2, 3];
items.Aggregate(10, (sum, current) => sum + current, x => x / 2F).Dump(); // (10 + 1 + 2 + 3) / 2f = 82
AggregateBy immediate execution โ
coming from .net9 ...
Element Operators โ
First immediate execution โ
FirstOrDefault immediate execution โ
IEnumerable<int> items = [];
items.FirstOrDefault().Dump(); // 0
items.FirstOrDefault(-1).Dump(); // -12
3
Single, SingleOrDefault and Last, LastOrDefault immediate execution โ
IEnumerable<int> items = [1, 2, 3];
IEnumerable<int> single = [1];
IEnumerable<int> empty = [];
items.Single().Dump(); // System.InvalidOperationException: Sequence contains more than one element
items.SingleOrDefault().Dump(); // System.InvalidOperationException: Sequence contains more than one element
items.SingleOrDefault(-1).Dump(); // System.InvalidOperationException: Sequence contains more than one element
single.Single().Dump(); // 1
single.SingleOrDefault().Dump(); // 1
single.SingleOrDefault(-1).Dump(); // 1
empty.Single().Dump(); // System.InvalidOperationException: Sequence contains no elements
empty.SingleOrDefault().Dump(); // 0
empty.SingleOrDefault(-1).Dump(); // 02
3
4
5
6
7
8
9
10
11
12
13
14
ElementAt and ElementAtOrDefault immediate execution โ
IEnumerable<int> items = [1, 2, 3];
items.ElementAt(100).Dump(); // System.IndexOutOfRangeException: Index was outside the bounds of the array.
items.ElementAtOrDefault(100).Dump(); // 02
3
DefaultIfEmpty โ
IEnumerable<int> items = [];
items.DefaultIfEmpty().Dump(); // [0]
items.DefaultIfEmpty().Count().Dump(); // 12
3
Conversion โ
ToLookUp immediate execution โ
IEnumerable<Person> people = [new("John", 30), new("Jane", 30), new("Modi", 18), new("John", 12)];
var a = people.ToLookup(x => x.Age).Dump();
people.ToLookup(x => x.Age)[30].Dump(); // John: 30, Jane: 30
people.ToLookup(x => x.Name)["John"].Dump(); // John: 30, John: 18
record class Person(string Name, int Age);2
3
4
5
Generators โ
Range immediate execution โ
Repeat immediate execution โ
Repeat a value for a number of times.
Enumerable.Repeat(-1, 3).Dump(); // [-1, -1, -1]Empty immediate execution โ
AsEnumerable โ
AsQueryable โ
Set Operations โ
Distinct โ
DistinctBy โ
Union โ
UnionBy โ
Intersect โ
IntersectBy โ
Except โ
Except only includes the elements in the first sequence but not in the second.
IEnumerable<int> a = [1, 2, 3];
IEnumerable<int> b = [2, 2, 3];
a.Except(b).Dump(); // [1]2
3
ExceptBy โ
SequenceEqual immediate execution โ
Returns if two sequences are equal by elements and their orders.
IEnumerable<int> a = [1, 2, 3];
IEnumerable<int> b = [2, 1, 3];
a.SequenceEqual(b).Dump(); // false
a.SequenceEqual(a).Dump(); // true2
3
4
Joining and Grouping โ
Zip โ
IEnumerable<int> a = ['a', 'b', 'c'];
IEnumerable<char> b = ['a', 'b', 'c'];
IEnumerable<char> c = ['a', 'b', 'c', 'd'];
a.Zip(b).Dump(); // tuples
a.Zip(c).Count().Dump(); // 3, zips minium count of any of two collection
a.Zip(b).First().GetType().Name.Dump(); // ValueTuple`2
a.Zip(b, c).Dump(); // ValueTuple`3
a.Zip(b, c).Count().Dump(); // 3
a.Zip(b, (x, y) => new { Number = x, Character = y }).Dump();2
3
4
5
6
7
8
9
Join โ
Join query two data sources and select them into results.
IEnumerable<Person> people =
[
new(1, "John", 30),
new(2, "Jane", 30),
new(3, "Modi", 18),
new(4, "John", 12)
];
IEnumerable<Weight> statuses =
[
new(1, 60),
new(2, 45),
new(3, 50),
new(4, 57)
];
people.Join(
statuses,
p => p.Id,
s => s.PersonId,
(p, s) => $"Person: {p.Name} weight {s.Kilogram} kilogram"
)
.Dump();
record class Person(int Id, string Name, int Age);
record class Weight(int PersonId, int Kilogram);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ <JoinIterator>d__129<Person, Weight, int, string> โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ "Person: John weight 60 kilogram" โ
โ "Person: Jane weight 45 kilogram" โ
โ "Person: Modi weight 50 kilogram" โ
โ "Person: John weight 57 kilogram" โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ2
3
4
5
6
7
8