• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CalionVarduk / LfrlAnvil / 9537274997

16 Jun 2024 04:07PM UTC coverage: 96.91% (-0.01%) from 96.922%
9537274997

push

github

CalionVarduk
Global:
- Update dotnet version to 8;
- Update C# version to 12;

MySql:
- Update MySqlConnector version to 2.3.7;

Sqlite:
- Update Microsoft.Data.Sqlite version to 8.0.6;

Tests:
- Update AutoFixture version to 4.18.1;
- Update Microsoft.NET.Test.Sdk version to 17.10.0;
- Update xunit & xunit.runner.visualstudio version to 2.8.1;

14633 of 15456 branches covered (94.68%)

Branch coverage included in aggregate %.

41659 of 42631 relevant lines covered (97.72%)

33821.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.04
/src/LfrlAnvil.Functional/Extensions/EnumerableExtensions.s.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Diagnostics.Contracts;
4
using System.Linq;
5
using System.Runtime.CompilerServices;
6
using LfrlAnvil.Extensions;
7

8
namespace LfrlAnvil.Functional.Extensions;
9

10
/// <summary>
11
/// Contains <see cref="IEnumerable{T}"/> extension methods.
12
/// </summary>
13
public static class EnumerableExtensions
14
{
15
    /// <summary>
16
    /// Filters out <see cref="Maybe{T}.None"/> elements from the provided <paramref name="source"/>.
17
    /// </summary>
18
    /// <param name="source">Source collection.</param>
19
    /// <typeparam name="T">Value type.</typeparam>
20
    /// <returns>New <see cref="IEnumerable{T}"/> with <see cref="Maybe{T}.None"/> elements filtered out.</returns>
21
    [Pure]
22
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
23
    public static IEnumerable<T> SelectValues<T>(this IEnumerable<Maybe<T>> source)
24
        where T : notnull
25
    {
26
        return source.Where( e => e.HasValue ).Select( e => e.Value! );
17✔
27
    }
28

29
    /// <summary>
30
    /// Filters out elements with second value from the provided <paramref name="source"/>.
31
    /// </summary>
32
    /// <param name="source">Source collection.</param>
33
    /// <typeparam name="T1">First either type.</typeparam>
34
    /// <typeparam name="T2">Second either type.</typeparam>
35
    /// <returns>New <see cref="IEnumerable{T}"/> with elements with second value filtered out.</returns>
36
    [Pure]
37
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
38
    public static IEnumerable<T1> SelectFirst<T1, T2>(this IEnumerable<Either<T1, T2>> source)
39
    {
40
        return source.Where( e => e.HasFirst ).Select( e => e.First! );
17✔
41
    }
42

43
    /// <summary>
44
    /// Filters out elements with first value from the provided <paramref name="source"/>.
45
    /// </summary>
46
    /// <param name="source">Source collection.</param>
47
    /// <typeparam name="T1">First either type.</typeparam>
48
    /// <typeparam name="T2">Second either type.</typeparam>
49
    /// <returns>New <see cref="IEnumerable{T}"/> with elements with first value filtered out.</returns>
50
    [Pure]
51
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
52
    public static IEnumerable<T2> SelectSecond<T1, T2>(this IEnumerable<Either<T1, T2>> source)
53
    {
54
        return source.Where( e => e.HasSecond ).Select( e => e.Second! );
17✔
55
    }
56

57
    /// <summary>
58
    /// Partitions the provided <paramref name="source"/> into separate collections that contain first and second values.
59
    /// </summary>
60
    /// <param name="source">Source collection.</param>
61
    /// <typeparam name="T1">First either type.</typeparam>
62
    /// <typeparam name="T2">Second either type.</typeparam>
63
    /// <returns>New tuple that contains partitioning result.</returns>
64
    [Pure]
65
    public static (List<T1> First, List<T2> Second) Partition<T1, T2>(this IEnumerable<Either<T1, T2>> source)
66
    {
67
        var first = new List<T1>();
1✔
68
        var second = new List<T2>();
1✔
69

70
        foreach ( var e in source )
14✔
71
        {
72
            if ( e.HasFirst )
6✔
73
                first.Add( e.First );
3✔
74
            else
75
                second.Add( e.Second );
3✔
76
        }
77

78
        return (first, second);
1✔
79
    }
80

81
    /// <summary>
82
    /// Filters out elements with errors from the provided <paramref name="source"/>.
83
    /// </summary>
84
    /// <param name="source">Source collection.</param>
85
    /// <typeparam name="T">Value type.</typeparam>
86
    /// <returns>New <see cref="IEnumerable{T}"/> with elements with errors filtered out.</returns>
87
    [Pure]
88
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
89
    public static IEnumerable<T> SelectValues<T>(this IEnumerable<Erratic<T>> source)
90
    {
91
        return source.Where( e => e.IsOk ).Select( e => e.Value! );
17✔
92
    }
93

94
    /// <summary>
95
    /// Filters out elements with values from the provided <paramref name="source"/>.
96
    /// </summary>
97
    /// <param name="source">Source collection.</param>
98
    /// <typeparam name="T">Value type.</typeparam>
99
    /// <returns>New <see cref="IEnumerable{T}"/> with elements with values filtered out.</returns>
100
    [Pure]
101
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
102
    public static IEnumerable<Exception> SelectErrors<T>(this IEnumerable<Erratic<T>> source)
103
    {
104
        return source.Where( e => e.HasError ).Select( e => e.Error! );
17✔
105
    }
106

107
    /// <summary>
108
    /// Partitions the provided <paramref name="source"/> into separate collections that contain values and errors.
109
    /// </summary>
110
    /// <param name="source">Source collection.</param>
111
    /// <typeparam name="T">Value type.</typeparam>
112
    /// <returns>New tuple that contains partitioning result.</returns>
113
    [Pure]
114
    public static (List<T> Values, List<Exception> Errors) Partition<T>(this IEnumerable<Erratic<T>> source)
115
    {
116
        var values = new List<T>();
1✔
117
        var errors = new List<Exception>();
1✔
118

119
        foreach ( var e in source )
14✔
120
        {
121
            if ( e.IsOk )
6✔
122
                values.Add( e.Value );
3✔
123
            else
124
                errors.Add( e.Error );
3✔
125
        }
126

127
        return (values, errors);
1✔
128
    }
129

130
    /// <summary>
131
    /// Attempts to find the minimum value in the provided <paramref name="source"/>
132
    /// by using the <see cref="Comparer{T}.Default"/> comparer.
133
    /// </summary>
134
    /// <param name="source">Source collection.</param>
135
    /// <typeparam name="T">Collection element type.</typeparam>
136
    /// <returns>
137
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
138
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
139
    /// </returns>
140
    [Pure]
141
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
142
    public static Maybe<T> TryMin<T>(this IEnumerable<T> source)
143
        where T : notnull
144
    {
145
        return source.TryMin( Comparer<T>.Default );
10✔
146
    }
147

148
    /// <summary>
149
    /// Attempts to find the minimum value in the provided <paramref name="source"/>.
150
    /// </summary>
151
    /// <param name="source">Source collection.</param>
152
    /// <param name="comparer">Comparer to use for element comparison.</param>
153
    /// <typeparam name="T">Collection element type.</typeparam>
154
    /// <returns>
155
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
156
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
157
    /// </returns>
158
    [Pure]
159
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
160
    public static Maybe<T> TryMin<T>(this IEnumerable<T> source, IComparer<T> comparer)
161
        where T : notnull
162
    {
163
        return source.TryMin( comparer, out var result ) ? new Maybe<T>( result ) : Maybe<T>.None;
10✔
164
    }
165

166
    /// <summary>
167
    /// Attempts to find the maximum value in the provided <paramref name="source"/>
168
    /// by using the <see cref="Comparer{T}.Default"/> comparer.
169
    /// </summary>
170
    /// <param name="source">Source collection.</param>
171
    /// <typeparam name="T">Collection element type.</typeparam>
172
    /// <returns>
173
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
174
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
175
    /// </returns>
176
    [Pure]
177
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
178
    public static Maybe<T> TryMax<T>(this IEnumerable<T> source)
179
        where T : notnull
180
    {
181
        return source.TryMax( Comparer<T>.Default );
10✔
182
    }
183

184
    /// <summary>
185
    /// Attempts to find the maximum value in the provided <paramref name="source"/>.
186
    /// </summary>
187
    /// <param name="source">Source collection.</param>
188
    /// <param name="comparer">Comparer to use for element comparison.</param>
189
    /// <typeparam name="T">Collection element type.</typeparam>
190
    /// <returns>
191
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
192
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
193
    /// </returns>
194
    [Pure]
195
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
196
    public static Maybe<T> TryMax<T>(this IEnumerable<T> source, IComparer<T> comparer)
197
        where T : notnull
198
    {
199
        return source.TryMax( comparer, out var result ) ? new Maybe<T>( result ) : Maybe<T>.None;
10✔
200
    }
201

202
    /// <summary>
203
    /// Attempts to compute an aggregation for the provided <paramref name="source"/>.
204
    /// </summary>
205
    /// <param name="source">Source collection.</param>
206
    /// <param name="func">Aggregator delegate.</param>
207
    /// <typeparam name="T">Collection element type.</typeparam>
208
    /// <returns>
209
    /// New <see cref="Maybe{T}"/> instance equivalent to the aggregation result
210
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
211
    /// </returns>
212
    [Pure]
213
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
214
    public static Maybe<T> TryAggregate<T>(this IEnumerable<T> source, Func<T, T, T> func)
215
        where T : notnull
216
    {
217
        return source.TryAggregate( func, out var result ) ? new Maybe<T>( result ) : Maybe<T>.None;
2✔
218
    }
219

220
    /// <summary>
221
    /// Attempts to find an element with the maximum value specified by the <paramref name="selector"/>
222
    /// in the provided <paramref name="source"/> using the <see cref="Comparer{T2}.Default"/> comparer.
223
    /// </summary>
224
    /// <param name="source">Source collection.</param>
225
    /// <param name="selector">Selector of a value to use for comparison.</param>
226
    /// <typeparam name="T1">Collection element type.</typeparam>
227
    /// <typeparam name="T2">Value type used for comparison.</typeparam>
228
    /// <returns>
229
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
230
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
231
    /// </returns>
232
    [Pure]
233
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
234
    public static Maybe<T1> TryMaxBy<T1, T2>(this IEnumerable<T1> source, Func<T1, T2> selector)
235
        where T1 : notnull
236
    {
237
        return source.TryMaxBy( selector, Comparer<T2>.Default );
10✔
238
    }
239

240
    /// <summary>
241
    /// Attempts to find an element with the maximum value specified by the <paramref name="selector"/>
242
    /// in the provided <paramref name="source"/>.
243
    /// </summary>
244
    /// <param name="source">Source collection.</param>
245
    /// <param name="selector">Selector of a value to use for comparison.</param>
246
    /// <param name="comparer">Comparer to use for value comparison.</param>
247
    /// <typeparam name="T1">Collection element type.</typeparam>
248
    /// <typeparam name="T2">Value type used for comparison.</typeparam>
249
    /// <returns>
250
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
251
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
252
    /// </returns>
253
    [Pure]
254
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
255
    public static Maybe<T1> TryMaxBy<T1, T2>(this IEnumerable<T1> source, Func<T1, T2> selector, IComparer<T2> comparer)
256
        where T1 : notnull
257
    {
258
        return source.TryMaxBy( selector, comparer, out var result ) ? new Maybe<T1>( result ) : Maybe<T1>.None;
10✔
259
    }
260

261
    /// <summary>
262
    /// Attempts to find an element with the minimum value specified by the <paramref name="selector"/>
263
    /// in the provided <paramref name="source"/> using the <see cref="Comparer{T2}.Default"/> comparer.
264
    /// </summary>
265
    /// <param name="source">Source collection.</param>
266
    /// <param name="selector">Selector of a value to use for comparison.</param>
267
    /// <typeparam name="T1">Collection element type.</typeparam>
268
    /// <typeparam name="T2">Value type used for comparison.</typeparam>
269
    /// <returns>
270
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
271
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
272
    /// </returns>
273
    [Pure]
274
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
275
    public static Maybe<T1> TryMinBy<T1, T2>(this IEnumerable<T1> source, Func<T1, T2> selector)
276
        where T1 : notnull
277
    {
278
        return source.TryMinBy( selector, Comparer<T2>.Default );
10✔
279
    }
280

281
    /// <summary>
282
    /// Attempts to find an element with the minimum value specified by the <paramref name="selector"/>
283
    /// in the provided <paramref name="source"/>.
284
    /// </summary>
285
    /// <param name="source">Source collection.</param>
286
    /// <param name="selector">Selector of a value to use for comparison.</param>
287
    /// <param name="comparer">Comparer to use for value comparison.</param>
288
    /// <typeparam name="T1">Collection element type.</typeparam>
289
    /// <typeparam name="T2">Value type used for comparison.</typeparam>
290
    /// <returns>
291
    /// New <see cref="Maybe{T}"/> instance equivalent to the found value
292
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
293
    /// </returns>
294
    [Pure]
295
    [MethodImpl( MethodImplOptions.AggressiveInlining )]
296
    public static Maybe<T1> TryMinBy<T1, T2>(this IEnumerable<T1> source, Func<T1, T2> selector, IComparer<T2> comparer)
297
        where T1 : notnull
298
    {
299
        return source.TryMinBy( selector, comparer, out var result ) ? new Maybe<T1>( result ) : Maybe<T1>.None;
10✔
300
    }
301

302
    /// <summary>
303
    /// Attempts to get the first element in the provided <paramref name="source"/>.
304
    /// </summary>
305
    /// <param name="source">Source collection.</param>
306
    /// <typeparam name="T">Collection element type.</typeparam>
307
    /// <returns>
308
    /// New <see cref="Maybe{T}"/> instance equivalent to the first element
309
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
310
    /// </returns>
311
    [Pure]
312
    public static Maybe<T> TryFirst<T>(this IEnumerable<T> source)
313
        where T : notnull
314
    {
315
        if ( source is IReadOnlyList<T> list )
6✔
316
            return list.Count > 0 ? new Maybe<T>( list[0] ) : Maybe<T>.None;
4✔
317

318
        using var enumerator = source.GetEnumerator();
2✔
319
        return enumerator.MoveNext() ? new Maybe<T>( enumerator.Current ) : Maybe<T>.None;
2!
320
    }
2✔
321

322
    /// <summary>
323
    /// Attempts to get the last element in the provided <paramref name="source"/>.
324
    /// </summary>
325
    /// <param name="source">Source collection.</param>
326
    /// <typeparam name="T">Collection element type.</typeparam>
327
    /// <returns>
328
    /// New <see cref="Maybe{T}"/> instance equivalent to the last element
329
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty.
330
    /// </returns>
331
    [Pure]
332
    public static Maybe<T> TryLast<T>(this IEnumerable<T> source)
333
        where T : notnull
334
    {
335
        if ( source is IReadOnlyList<T> list )
6✔
336
            return list.Count > 0 ? new Maybe<T>( list[^1] ) : Maybe<T>.None;
4✔
337

338
        using var enumerator = source.GetEnumerator();
2✔
339

340
        if ( ! enumerator.MoveNext() )
2!
341
            return Maybe<T>.None;
×
342

343
        var last = enumerator.Current;
2✔
344
        while ( enumerator.MoveNext() )
5✔
345
            last = enumerator.Current;
3✔
346

347
        return new Maybe<T>( last );
2✔
348
    }
2✔
349

350
    /// <summary>
351
    /// Attempts to get the only element in the provided <paramref name="source"/>.
352
    /// </summary>
353
    /// <param name="source">Source collection.</param>
354
    /// <typeparam name="T">Collection element type.</typeparam>
355
    /// <returns>
356
    /// New <see cref="Maybe{T}"/> instance equivalent to the only element
357
    /// or <see cref="Maybe{T}.None"/> when <paramref name="source"/> is empty or contains more than <b>1</b> element.
358
    /// </returns>
359
    [Pure]
360
    public static Maybe<T> TrySingle<T>(this IEnumerable<T> source)
361
        where T : notnull
362
    {
363
        if ( source is IReadOnlyList<T> list )
6✔
364
            return list.Count == 1 ? new Maybe<T>( list[0] ) : Maybe<T>.None;
4✔
365

366
        using var enumerator = source.GetEnumerator();
2✔
367

368
        if ( ! enumerator.MoveNext() )
2!
369
            return Maybe<T>.None;
×
370

371
        var candidate = enumerator.Current;
2✔
372
        return enumerator.MoveNext() ? Maybe<T>.None : new Maybe<T>( candidate );
2✔
373
    }
2✔
374

375
    /// <summary>
376
    /// Attempts to get an element at the specified <paramref name="index"/> in the provided <paramref name="source"/>.
377
    /// </summary>
378
    /// <param name="source">Source collection.</param>
379
    /// <param name="index">0-based position of an element to find.</param>
380
    /// <typeparam name="T">Collection element type.</typeparam>
381
    /// <returns>
382
    /// New <see cref="Maybe{T}"/> instance equivalent to the element at the specified position
383
    /// or <see cref="Maybe{T}.None"/> when <paramref name="index"/> is not in [<b>0</b>, <paramref name="source"/> count) range.
384
    /// </returns>
385
    [Pure]
386
    public static Maybe<T> TryElementAt<T>(this IEnumerable<T> source, int index)
387
        where T : notnull
388
    {
389
        if ( index < 0 )
15✔
390
            return Maybe<T>.None;
1✔
391

392
        if ( source is IReadOnlyList<T> list )
14✔
393
            return index < list.Count ? new Maybe<T>( list[index] ) : Maybe<T>.None;
8✔
394

395
        using var enumerator = source.GetEnumerator();
6✔
396

397
        if ( ! enumerator.MoveNext() )
6!
398
            return Maybe<T>.None;
×
399

400
        if ( index == 0 )
6✔
401
            return new Maybe<T>( enumerator.Current );
1✔
402

403
        --index;
5✔
404
        while ( enumerator.MoveNext() )
9✔
405
        {
406
            if ( index == 0 )
6✔
407
                return new Maybe<T>( enumerator.Current );
2✔
408

409
            --index;
4✔
410
        }
411

412
        return Maybe<T>.None;
3✔
413
    }
6✔
414
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc