-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Segment.cs
136 lines (121 loc) · 4.89 KB
/
Segment.cs
1
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
namespace SuperLinq;
public static partial class SuperEnumerable
{
/// <summary>
/// Divides a sequence into multiple sequences by using a segment detector based on the original sequence
/// </summary>
/// <typeparam name="T">
/// The type of the elements in the sequence
/// </typeparam>
/// <param name="source">
/// The sequence to segment
/// </param>
/// <param name="newSegmentPredicate">
/// A function, which returns <see langword="true"/> if the given element begins a new segment, and <see
/// langword="false"/> otherwise
/// </param>
/// <returns>
/// A sequence of segment, each of which is a portion of the original sequence
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/> or <paramref name="newSegmentPredicate"/> is <see langword="null"/>.
/// </exception>
/// <remarks>
/// <para>
/// This method is implemented by using deferred execution and streams the groupings. The grouping elements,
/// however, are buffered. Each grouping is therefore yielded as soon as it is complete and before the next
/// grouping occurs.
/// </para>
/// </remarks>
public static IEnumerable<IReadOnlyList<T>> Segment<T>(this IEnumerable<T> source, Func<T, bool> newSegmentPredicate)
{
ArgumentNullException.ThrowIfNull(newSegmentPredicate);
return Segment(source, (curr, prev, index) => newSegmentPredicate(curr));
}
/// <summary>
/// Divides a sequence into multiple sequences by using a segment detector based on the original sequence
/// </summary>
/// <typeparam name="T">
/// The type of the elements in the sequence
/// </typeparam>
/// <param name="source">
/// The sequence to segment
/// </param>
/// <param name="newSegmentPredicate">
/// A function, which returns <see langword="true"/> if the given element and index begins a new segment, and
/// <see langword="false"/> otherwise
/// </param>
/// <returns>
/// A sequence of segment, each of which is a portion of the original sequence
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/> or <paramref name="newSegmentPredicate"/> is <see langword="null"/>.
/// </exception>
/// <remarks>
/// <para>
/// This method is implemented by using deferred execution and streams the groupings. The grouping elements,
/// however, are buffered. Each grouping is therefore yielded as soon as it is complete and before the next
/// grouping occurs.
/// </para>
/// </remarks>
public static IEnumerable<IReadOnlyList<T>> Segment<T>(this IEnumerable<T> source, Func<T, int, bool> newSegmentPredicate)
{
ArgumentNullException.ThrowIfNull(newSegmentPredicate);
return Segment(source, (curr, prev, index) => newSegmentPredicate(curr, index));
}
/// <summary>
/// Divides a sequence into multiple sequences by using a segment detector based on the original sequence
/// </summary>
/// <typeparam name="T">
/// The type of the elements in the sequence
/// </typeparam>
/// <param name="source">
/// The sequence to segment
/// </param>
/// <param name="newSegmentPredicate">
/// A function, which returns <see langword="true"/> if the given current element, previous element, and index
/// begins a new segment, and <see langword="false"/> otherwise
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/> or <paramref name="newSegmentPredicate"/> is <see langword="null"/>.
/// </exception>
/// <returns>
/// A sequence of segment, each of which is a portion of the original sequence
/// </returns>
/// <remarks>
/// <para>
/// This method is implemented by using deferred execution and streams the groupings. The grouping elements,
/// however, are buffered. Each grouping is therefore yielded as soon as it is complete and before the next
/// grouping occurs.
/// </para>
/// </remarks>
public static IEnumerable<IReadOnlyList<T>> Segment<T>(this IEnumerable<T> source, Func<T, T, int, bool> newSegmentPredicate)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(newSegmentPredicate);
return Core(source, newSegmentPredicate);
static IEnumerable<IReadOnlyList<T>> Core(IEnumerable<T> source, Func<T, T, int, bool> newSegmentPredicate)
{
using var e = source.GetEnumerator();
if (!e.MoveNext()) // break early (it's empty)
yield break;
// Ensure that the first item is always part of the first
// segment. This is an intentional behavior. Segmentation always
// begins with the second element in the sequence.
var previous = e.Current;
var segment = new List<T> { previous };
for (var index = 1; e.MoveNext(); index++)
{
var current = e.Current;
if (newSegmentPredicate(current, previous, index))
{
yield return segment;
segment = [];
}
segment.Add(current);
previous = current;
}
yield return segment;
}
}
}