@@ -3,6 +3,8 @@ package glinq
33import "sort"
44
55// Where filters elements by predicate.
6+ //
7+ // SIZE: Loses size (unknown how many elements pass filter).
68func (s * stream [T ]) Where (predicate func (T ) bool ) Stream [T ] {
79 return & stream [T ]{
810 sourceFactory : func () func () (T , bool ) {
@@ -20,12 +22,15 @@ func (s *stream[T]) Where(predicate func(T) bool) Stream[T] {
2022 }
2123 }
2224 },
25+ size : nil , // LOSE: unknown how many pass filter
2326 }
2427}
2528
2629// Select transforms elements to the same type.
2730// Supports method chaining.
2831//
32+ // SIZE: Preserves size (1-to-1 transformation).
33+ //
2934// Example:
3035//
3136// doubled := From([]int{1, 2, 3}).
@@ -45,12 +50,15 @@ func (s *stream[T]) Select(mapper func(T) T) Stream[T] {
4550 return mapper (value ), true
4651 }
4752 },
53+ size : s .size , // PRESERVE: 1-to-1 transformation
4854 }
4955}
5056
5157// SelectWithIndex transforms elements to the same type, providing index to mapper function.
5258// Supports method chaining.
5359//
60+ // SIZE: Preserves size (1-to-1 transformation).
61+ //
5462// Example:
5563//
5664// doubled := From([]int{1, 2, 3}).
@@ -73,12 +81,15 @@ func (s *stream[T]) SelectWithIndex(mapper func(T, int) T) Stream[T] {
7381 return result , true
7482 }
7583 },
84+ size : s .size , // PRESERVE: 1-to-1 transformation
7685 }
7786}
7887
7988// Map transforms elements to a different type.
8089// This is a function (not a method) because in Go methods cannot have their own type parameters.
8190//
91+ // SIZE: Preserves size if source is Sizable (1-to-1 transformation).
92+ //
8293// Example:
8394//
8495// strings := Select(
@@ -87,6 +98,12 @@ func (s *stream[T]) SelectWithIndex(mapper func(T, int) T) Stream[T] {
8798// ).ToSlice()
8899// // []string{"num_1", "num_2", "num_3"}
89100func Select [T , R any ](enum Enumerable [T ], mapper func (T ) R ) Stream [R ] {
101+ var size * int
102+ if sizable , ok := enum .(Sizable [T ]); ok {
103+ if s , known := sizable .Size (); known {
104+ size = & s
105+ }
106+ }
90107 return & stream [R ]{
91108 sourceFactory : func () func () (R , bool ) {
92109 return func () (R , bool ) {
@@ -98,12 +115,15 @@ func Select[T, R any](enum Enumerable[T], mapper func(T) R) Stream[R] {
98115 return mapper (value ), true
99116 }
100117 },
118+ size : size , // PRESERVE if possible
101119 }
102120}
103121
104122// SelectWithIndex transforms elements to a different type, providing index to mapper function.
105123// This is a function (not a method) because in Go methods cannot have their own type parameters.
106124//
125+ // SIZE: Preserves size if source is Sizable (1-to-1 transformation).
126+ //
107127// Example:
108128//
109129// strings := SelectWithIndex(
@@ -112,6 +132,12 @@ func Select[T, R any](enum Enumerable[T], mapper func(T) R) Stream[R] {
112132// ).ToSlice()
113133// // []string{"num_1_at_0", "num_2_at_1", "num_3_at_2"}
114134func SelectWithIndex [T , R any ](enum Enumerable [T ], mapper func (T , int ) R ) Stream [R ] {
135+ var size * int
136+ if sizable , ok := enum .(Sizable [T ]); ok {
137+ if s , known := sizable .Size (); known {
138+ size = & s
139+ }
140+ }
115141 return & stream [R ]{
116142 sourceFactory : func () func () (R , bool ) {
117143 index := 0 // Fresh counter
@@ -126,11 +152,26 @@ func SelectWithIndex[T, R any](enum Enumerable[T], mapper func(T, int) R) Stream
126152 return result , true
127153 }
128154 },
155+ size : size , // PRESERVE if possible
129156 }
130157}
131158
132159// Take takes the first n elements from Stream.
160+ //
161+ // SIZE: Calculated as min(sourceSize, n) if source size known, else n.
133162func (s * stream [T ]) Take (n int ) Stream [T ] {
163+ var newSize * int
164+ if s .size != nil {
165+ size := * s .size
166+ if size < n {
167+ newSize = & size
168+ } else {
169+ newSize = & n
170+ }
171+ } else {
172+ newSize = & n // Don't know source size, but result won't exceed n
173+ }
174+
134175 return & stream [T ]{
135176 sourceFactory : func () func () (T , bool ) {
136177 source := s .sourceFactory () // Get fresh source
@@ -149,11 +190,24 @@ func (s *stream[T]) Take(n int) Stream[T] {
149190 return value , true
150191 }
151192 },
193+ size : newSize , // CALCULATED: min(source, n)
152194 }
153195}
154196
155197// Skip skips the first n elements from Stream.
198+ //
199+ // SIZE: Calculated as max(0, sourceSize - n) if source size known, else unknown.
156200func (s * stream [T ]) Skip (n int ) Stream [T ] {
201+ var newSize * int
202+ if s .size != nil {
203+ size := * s .size - n
204+ if size < 0 {
205+ size = 0
206+ }
207+ newSize = & size
208+ }
209+ // else: nil (unknown)
210+
157211 return & stream [T ]{
158212 sourceFactory : func () func () (T , bool ) {
159213 source := s .sourceFactory () // Get fresh source
@@ -170,6 +224,7 @@ func (s *stream[T]) Skip(n int) Stream[T] {
170224 return source ()
171225 }
172226 },
227+ size : newSize , // CALCULATED: max(0, source - n) or nil
173228 }
174229}
175230
@@ -207,6 +262,8 @@ func heapifyDown[T any](items []T, i, n int, less func(a, b T) bool) {
207262// TakeOrderedBy returns the first n elements ordered by the less function.
208263// Uses a heap-based algorithm for efficient processing.
209264//
265+ // SIZE: Calculated as min(sourceSize, n) if source size known, else n.
266+ //
210267// Example:
211268//
212269// numbers := []int{5, 2, 8, 1, 9, 3}
@@ -217,6 +274,21 @@ func TakeOrderedBy[T any](enum Enumerable[T], n int, less func(a, b T) bool) Str
217274 return Empty [T ]()
218275 }
219276
277+ var size * int
278+ if sizable , ok := enum .(Sizable [T ]); ok {
279+ if s , known := sizable .Size (); known {
280+ if s < n {
281+ size = & s
282+ } else {
283+ size = & n
284+ }
285+ } else {
286+ size = & n // Don't know source size, but result won't exceed n
287+ }
288+ } else {
289+ size = & n // Don't know source size, but result won't exceed n
290+ }
291+
220292 return & stream [T ]{
221293 sourceFactory : func () func () (T , bool ) {
222294 var heap []T
@@ -269,6 +341,7 @@ func TakeOrderedBy[T any](enum Enumerable[T], n int, less func(a, b T) bool) Str
269341 return result , true
270342 }
271343 },
344+ size : size , // CALCULATED: min(source, n)
272345 }
273346}
274347
@@ -286,17 +359,21 @@ func TakeOrderedDescendingBy[T any](enum Enumerable[T], n int, less func(a, b T)
286359
287360// Reverse reverses the order of elements in the Stream.
288361// NOTE: Reverse materializes the entire stream (partially lazy).
362+ //
363+ // SIZE: Preserves size (1-to-1 transformation, materializes).
289364func (s * stream [T ]) Reverse () Stream [T ] {
290365 items := s .ToSlice ()
291366 for i , j := 0 , len (items )- 1 ; i < j ; i , j = i + 1 , j - 1 {
292367 items [i ], items [j ] = items [j ], items [i ]
293368 }
294- return From (items )
369+ return From (items ) // From preserves size
295370}
296371
297372// SelectMany transforms each element into a sequence and flattens the resulting sequences.
298373// This is a function (not a method) because in Go methods cannot have their own type parameters.
299374//
375+ // SIZE: Loses size (1-to-many transformation, unknown result count).
376+ //
300377// Example:
301378//
302379// numbers := [][]int{{1, 2}, {3, 4}, {5}}
@@ -336,5 +413,6 @@ func SelectMany[T, R any](enum Enumerable[T], selector func(T) Enumerable[R]) St
336413 }
337414 }
338415 },
416+ size : nil , // LOSE: 1-to-many transformation
339417 }
340418}
0 commit comments