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>(); // error
2
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(); // 3
2
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(); // false
2
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(); // 31
2
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 = 8
2
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(); // -1
2
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(); // 0
2
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(); // 0
2
3
DefaultIfEmpty
โ
IEnumerable<int> items = [];
items.DefaultIfEmpty().Dump(); // [0]
items.DefaultIfEmpty().Count().Dump(); // 1
2
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(); // true
2
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