BadScript 2
Loading...
Searching...
No Matches
EnumerableExtensions.cs
Go to the documentation of this file.
1//#define CSX_ENUM_INTERNAL // Uncomment or define at build time to set accessibility to internal.
2//#define CSX_REM_MAYBE_FUNC // Uncomment or define at build time to remove dependency to Maybe.cs.
3//#define CSX_REM_CRYPTORAND // Uncomment or define at build time to remove dependency to CryptoRandom.cs.
4
5using System;
6using System.Collections;
7using System.Collections.Generic;
8using System.Diagnostics;
9using System.Globalization;
10using System.Linq;
11using System.Text;
12
13using LinqEnumerable = System.Linq.Enumerable;
14
15namespace CSharpx
16{
17#if !CSX_ENUM_INTERNAL
18 public
19#endif
20 internal static class EnumerableExtensions
21 {
22 private static readonly Func<int, int, Exception> OnFolderSourceSizeErrorSelector = OnFolderSourceSizeError;
23
24 private static IEnumerable<TSource> AssertCountImpl<TSource>(IEnumerable<TSource> source,
25 int count,
26 Func<int, int, Exception> errorSelector)
27 {
28 ICollection<TSource> collection = source as ICollection<TSource>; // Optimization for collections
29
30 if (collection != null)
31 {
32 if (collection.Count != count)
33 {
34 throw errorSelector(collection.Count.CompareTo(count), count);
35 }
36
37 return source;
38 }
39
40 return ExpectingCountYieldingImpl(source, count, errorSelector);
41 }
42
43 private static IEnumerable<TSource> ExpectingCountYieldingImpl<TSource>(IEnumerable<TSource> source,
44 int count,
45 Func<int, int, Exception> errorSelector)
46 {
47 int iterations = 0;
48
49 foreach (TSource element in source)
50 {
51 iterations++;
52
53 if (iterations > count)
54 {
55 throw errorSelector(1, count);
56 }
57
58 yield return element;
59 }
60
61 if (iterations != count)
62 {
63 throw errorSelector(-1, count);
64 }
65 }
66
71 public static IEnumerable<TResult> Cartesian<TFirst, TSecond, TResult>(
72 this IEnumerable<TFirst> first,
73 IEnumerable<TSecond> second,
74 Func<TFirst, TSecond, TResult> resultSelector)
75 {
76 if (first == null)
77 {
78 throw new ArgumentNullException(nameof(first));
79 }
80
81 if (second == null)
82 {
83 throw new ArgumentNullException(nameof(second));
84 }
85
86 if (resultSelector == null)
87 {
88 throw new ArgumentNullException(nameof(resultSelector));
89 }
90
91 return from element1 in first
92 from element2 in second // TODO buffer to avoid multiple enumerations
93 select resultSelector(element1, element2);
94 }
95
99 public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource value)
100 {
101 if (source == null)
102 {
103 throw new ArgumentNullException(nameof(source));
104 }
105
106 return LinqEnumerable.Repeat(value, 1)
107 .Concat(source);
108 }
109
113 public static IEnumerable<T> Concat<T>(this T head, IEnumerable<T> tail)
114 {
115 if (tail == null)
116 {
117 throw new ArgumentNullException(nameof(tail));
118 }
119
120 return tail.Prepend(head);
121 }
122
126 public static IEnumerable<T> Concat<T>(this IEnumerable<T> head, T tail)
127 {
128 if (head == null)
129 {
130 throw new ArgumentNullException(nameof(head));
131 }
132
133 return head.Concat(LinqEnumerable.Repeat(tail, 1));
134 }
135
140 public static IEnumerable<T> Exclude<T>(this IEnumerable<T> sequence, int startIndex, int count)
141 {
142 if (sequence == null)
143 {
144 throw new ArgumentNullException(nameof(sequence));
145 }
146
147 if (startIndex < 0)
148 {
149 throw new ArgumentOutOfRangeException(nameof(startIndex));
150 }
151
152 if (count < 0)
153 {
154 throw new ArgumentOutOfRangeException(nameof(count));
155 }
156
157 return ExcludeImpl(sequence, startIndex, count);
158 }
159
160 private static IEnumerable<T> ExcludeImpl<T>(IEnumerable<T> sequence, int startIndex, int count)
161 {
162 int index = -1;
163 int endIndex = startIndex + count;
164
165 using (IEnumerator<T> iter = sequence.GetEnumerator())
166 {
167 // yield the first part of the sequence
168 while (iter.MoveNext() && ++index < startIndex)
169 {
170 yield return iter.Current;
171 }
172
173 // skip the next part (up to count elements)
174 while (++index < endIndex && iter.MoveNext()) { }
175
176 // yield the remainder of the sequence
177 while (iter.MoveNext())
178 {
179 yield return iter.Current;
180 }
181 }
182 }
183
189 public static IEnumerable<KeyValuePair<int, TSource>> Index<TSource>(this IEnumerable<TSource> source)
190 {
191 return source.Index(0);
192 }
193
199 public static IEnumerable<KeyValuePair<int, TSource>> Index<TSource>(
200 this IEnumerable<TSource> source,
201 int startIndex)
202 {
203 return source.Select((element, index) => new KeyValuePair<int, TSource>(startIndex + index, element));
204 }
205
210 public static TResult Fold<T, TResult>(this IEnumerable<T> source, Func<T, TResult> folder)
211 {
212 return FoldImpl(source, 1, folder, null, null, null);
213 }
214
219 public static TResult Fold<T, TResult>(this IEnumerable<T> source, Func<T, T, TResult> folder)
220 {
221 return FoldImpl(source, 2, null, folder, null, null);
222 }
223
228 public static TResult Fold<T, TResult>(this IEnumerable<T> source, Func<T, T, T, TResult> folder)
229 {
230 return FoldImpl(source, 3, null, null, folder, null);
231 }
232
237 public static TResult Fold<T, TResult>(this IEnumerable<T> source, Func<T, T, T, T, TResult> folder)
238 {
239 return FoldImpl(source, 4, null, null, null, folder);
240 }
241
242 private static TResult FoldImpl<T, TResult>(IEnumerable<T> source,
243 int count,
244 Func<T, TResult> folder1,
245 Func<T, T, TResult> folder2,
246 Func<T, T, T, TResult> folder3,
247 Func<T, T, T, T, TResult> folder4)
248 {
249 if (source == null)
250 {
251 throw new ArgumentNullException(nameof(source));
252 }
253
254 if ((count == 1 && folder1 == null) ||
255 (count == 2 && folder2 == null) ||
256 (count == 3 && folder3 == null) ||
257 (count == 4 && folder4 == null))
258 {
259 // ReSharper disable NotResolvedInText
260 throw new ArgumentNullException("folder"); // ReSharper restore NotResolvedInText
261 }
262
263 T[] elements = new T[count];
264
265 foreach (KeyValuePair<int, T> e in AssertCountImpl(source.Index(),
266 count,
268 ))
269 {
270 elements[e.Key] = e.Value;
271 }
272
273 switch (count)
274 {
275 case 1:
276 return folder1(elements[0]);
277 case 2:
278 return folder2(elements[0], elements[1]);
279 case 3:
280 return folder3(elements[0], elements[1], elements[2]);
281 case 4:
282 return folder4(elements[0], elements[1], elements[2], elements[3]);
283 default:
284 throw new NotSupportedException();
285 }
286 }
287
288 private static Exception OnFolderSourceSizeError(int cmp, int count)
289 {
290 string message = cmp < 0
291 ? "Sequence contains too few elements when exactly {0} {1} expected"
292 : "Sequence contains too many elements when exactly {0} {1} expected";
293
294 return new Exception(string.Format(message, count.ToString("N0"), count == 1 ? "was" : "were"));
295 }
296
300 public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
301 {
302 if (source == null)
303 {
304 throw new ArgumentNullException(nameof(source));
305 }
306
307 if (action == null)
308 {
309 throw new ArgumentNullException(nameof(action));
310 }
311
312 foreach (T element in source)
313 {
314 action(element);
315 }
316 }
317
324 public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> source,
325 Func<TSource, TSource, TResult> resultSelector)
326 {
327 if (source == null)
328 {
329 throw new ArgumentNullException(nameof(source));
330 }
331
332 if (resultSelector == null)
333 {
334 throw new ArgumentNullException(nameof(resultSelector));
335 }
336
337 return PairwiseImpl(source, resultSelector);
338 }
339
340 private static IEnumerable<TResult> PairwiseImpl<TSource, TResult>(
341 this IEnumerable<TSource> source,
342 Func<TSource, TSource, TResult> resultSelector)
343 {
344 Debug.Assert(source != null);
345 Debug.Assert(resultSelector != null);
346
347 using (IEnumerator<TSource> e = source.GetEnumerator())
348 {
349 if (!e.MoveNext())
350 {
351 yield break;
352 }
353
354 TSource previous = e.Current;
355
356 while (e.MoveNext())
357 {
358 yield return resultSelector(previous, e.Current);
359 previous = e.Current;
360 }
361 }
362 }
363
368 public static string ToDelimitedString<TSource>(this IEnumerable<TSource> source)
369 {
370 return ToDelimitedString(source, null);
371 }
372
377 public static string ToDelimitedString<TSource>(this IEnumerable<TSource> source, string delimiter)
378 {
379 if (source == null)
380 {
381 throw new ArgumentNullException(nameof(source));
382 }
383
384 return ToDelimitedStringImpl(source, delimiter, (sb, e) => sb.Append(e));
385 }
386
387 private static string ToDelimitedStringImpl<T>(IEnumerable<T> source,
388 string delimiter,
389 Func<StringBuilder, T, StringBuilder> append)
390 {
391 Debug.Assert(source != null);
392 Debug.Assert(append != null);
393
394 delimiter = delimiter ?? CultureInfo.CurrentCulture.TextInfo.ListSeparator;
395 StringBuilder sb = new StringBuilder();
396 int i = 0;
397
398 foreach (T value in source)
399 {
400 if (i++ > 0)
401 {
402 sb.Append(delimiter);
403 }
404
405 append(sb, value);
406 }
407
408 return sb.ToString();
409 }
410
414 public static IEnumerable<T> Tail<T>(this IEnumerable<T> source)
415 {
416 using (IEnumerator<T> e = source.GetEnumerator())
417 {
418 if (e.MoveNext())
419 {
420 while (e.MoveNext())
421 {
422 yield return e.Current;
423 }
424 }
425 else
426 {
427 throw new ArgumentException("Source sequence cannot be empty", nameof(source));
428 }
429 }
430 }
431
435 public static IEnumerable<T> TailNoFail<T>(this IEnumerable<T> source)
436 {
437 using (IEnumerator<T> e = source.GetEnumerator())
438 {
439 if (e.MoveNext())
440 {
441 while (e.MoveNext())
442 {
443 yield return e.Current;
444 }
445 }
446 }
447 }
448
452 public static IEnumerable<T> Memoize<T>(this IEnumerable<T> source)
453 {
454 return source.GetType()
455 .IsArray
456 ? source
457 : source.ToArray();
458 }
459
463 public static IEnumerable<T> Materialize<T>(this IEnumerable<T> source)
464 {
465 if (source is MaterializedEnumerable<T> ||
466 source.GetType()
467 .IsArray)
468 {
469 return source;
470 }
471
472 return new MaterializedEnumerable<T>(source);
473 }
474
478 public static T Choice<T>(this IEnumerable<T> source)
479 {
480#if CSX_REM_CRYPTORAND
481 int index = new Random().Next(source.Count() - 1);
482#else
483 var index = new CryptoRandom().Next(source.Count() - 1);
484#endif
485 return source.ElementAt(index);
486 }
487
491 public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T element)
492 {
493 if (element == null)
494 {
495 throw new ArgumentNullException(nameof(element));
496 }
497
498 int count = source.Count();
499 int last = count - 1;
500
501 for (int i = 0; i < count; i++)
502 {
503 yield return source.ElementAt(i);
504
505 if (i != last)
506 {
507 yield return element;
508 }
509 }
510 }
511
515 public static IEnumerable<T> FlattenOnce<T>(this IEnumerable<IEnumerable<T>> source)
516 {
517 foreach (IEnumerable<T> element in source)
518 {
519 foreach (T subelement in element)
520 {
521 yield return subelement;
522 }
523 }
524 }
525
530 public static IEnumerable<string> FlattenOnce(this IEnumerable<string> source)
531 {
532 foreach (string element in source)
533 {
534 string[] parts = element.Split();
535
536 foreach (string part in parts)
537 {
538 yield return part;
539 }
540 }
541 }
542
543#region Nested type: MaterializedEnumerable
544
545 private class MaterializedEnumerable<T> : IEnumerable<T>
546 {
547 private readonly ICollection<T> inner;
548
549 public MaterializedEnumerable(IEnumerable<T> enumerable)
550 {
551 inner = enumerable as ICollection<T> ?? enumerable.ToArray();
552 }
553
554#region IEnumerable<T> Members
555
556 public IEnumerator<T> GetEnumerator()
557 {
558 return inner.GetEnumerator();
559 }
560
561 IEnumerator IEnumerable.GetEnumerator()
562 {
563 return GetEnumerator();
564 }
565
566#endregion
567 }
568
569#endregion
570
571#if !CSX_REM_MAYBE_FUNC
575 public static Maybe<T> TryHead<T>(this IEnumerable<T> source)
576 {
577 using (IEnumerator<T> e = source.GetEnumerator())
578 {
579 return e.MoveNext()
580 ? Maybe.Just(e.Current)
581 : Maybe.Nothing<T>();
582 }
583 }
584
588 public static Maybe<IEnumerable<T>> ToMaybe<T>(this IEnumerable<T> source)
589 {
590 using (IEnumerator<T> e = source.GetEnumerator())
591 {
592 return e.MoveNext()
593 ? Maybe.Just(source)
594 : Maybe.Nothing<IEnumerable<T>>();
595 }
596 }
597#endif
598 }
599}
System.Linq.Enumerable LinqEnumerable
static IEnumerable< T > Concat< T >(this T head, IEnumerable< T > tail)
Returns a sequence consisting of the head element and the given tail elements.
static string ToDelimitedStringImpl< T >(IEnumerable< T > source, string delimiter, Func< StringBuilder, T, StringBuilder > append)
static T Choice< T >(this IEnumerable< T > source)
Selects a random element.
static Maybe< IEnumerable< T > > ToMaybe< T >(this IEnumerable< T > source)
Turns an empty sequence to Nothing, otherwise Just(sequence).
static Exception OnFolderSourceSizeError(int cmp, int count)
static IEnumerable< T > Memoize< T >(this IEnumerable< T > source)
Captures current state of a sequence.
static IEnumerable< KeyValuePair< int, TSource > > Index< TSource >(this IEnumerable< TSource > source)
Returns a sequence of KeyValuePair<TKey,TValue> where the key is the zero-based index of the value in...
static string ToDelimitedString< TSource >(this IEnumerable< TSource > source)
Creates a delimited string from a sequence of values. The delimiter used depends on the current cultu...
static TResult Fold< T, TResult >(this IEnumerable< T > source, Func< T, TResult > folder)
Returns the result of applying a function to a sequence of 1 element.
static IEnumerable< TSource > ExpectingCountYieldingImpl< TSource >(IEnumerable< TSource > source, int count, Func< int, int, Exception > errorSelector)
static IEnumerable< T > FlattenOnce< T >(this IEnumerable< IEnumerable< T > > source)
Flattens a sequence by one level.
static IEnumerable< T > Tail< T >(this IEnumerable< T > source)
Return everything except first element and throws exception if empty.
static TResult FoldImpl< T, TResult >(IEnumerable< T > source, int count, Func< T, TResult > folder1, Func< T, T, TResult > folder2, Func< T, T, T, TResult > folder3, Func< T, T, T, T, TResult > folder4)
static IEnumerable< T > Intersperse< T >(this IEnumerable< T > source, T element)
Takes an element and a sequence and ‘intersperses’ that element between its elements.
static readonly Func< int, int, Exception > OnFolderSourceSizeErrorSelector
static IEnumerable< string > FlattenOnce(this IEnumerable< string > source)
Reduces a sequence of strings to a sequence of parts, splitted by space, of each original string.
static IEnumerable< T > Materialize< T >(this IEnumerable< T > source)
Creates an immutable copy of a sequence.
static IEnumerable< TResult > Cartesian< TFirst, TSecond, TResult >(this IEnumerable< TFirst > first, IEnumerable< TSecond > second, Func< TFirst, TSecond, TResult > resultSelector)
Returns the Cartesian product of two sequences by combining each element of the first set with each i...
static Maybe< T > TryHead< T >(this IEnumerable< T > source)
Safe function that returns Just(first element) or None.
static IEnumerable< TSource > AssertCountImpl< TSource >(IEnumerable< TSource > source, int count, Func< int, int, Exception > errorSelector)
static IEnumerable< T > Exclude< T >(this IEnumerable< T > sequence, int startIndex, int count)
Excludes count elements from a sequence starting at a given index.
static void ForEach< T >(this IEnumerable< T > source, Action< T > action)
Immediately executes the given action on each element in the source sequence.
static IEnumerable< T > TailNoFail< T >(this IEnumerable< T > source)
Return everything except first element without throwing exception if empty.
static IEnumerable< TResult > PairwiseImpl< TSource, TResult >(this IEnumerable< TSource > source, Func< TSource, TSource, TResult > resultSelector)
static IEnumerable< TSource > Prepend< TSource >(this IEnumerable< TSource > source, TSource value)
Prepends a single value to a sequence.
static IEnumerable< T > ExcludeImpl< T >(IEnumerable< T > sequence, int startIndex, int count)
static IEnumerable< TResult > Pairwise< TSource, TResult >(this IEnumerable< TSource > source, Func< TSource, TSource, TResult > resultSelector)
Returns a sequence resulting from applying a function to each element in the source sequence and its ...
The Maybe type models an optional value. A value of type Maybe a either contains a value of type a (r...
Definition Maybe.cs:33