Skip to content

Commit 2a76d42

Browse files
author
Roman Sorokin
committed
fix structs size
1 parent 39c552b commit 2a76d42

File tree

8 files changed

+129
-72
lines changed

8 files changed

+129
-72
lines changed

pkg/glinq/distinct.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func Distinct[T comparable](enum Enumerable[T]) Stream[T] {
2525
}
2626
}
2727
},
28-
size: nil, // LOSE: unknown how many duplicates
28+
size: -1, // LOSE: unknown how many duplicates
2929
}
3030
}
3131

@@ -54,6 +54,6 @@ func (s *stream[T]) DistinctBy(keySelector func(T) any) Stream[T] {
5454
}
5555
}
5656
},
57-
size: nil, // LOSE: unknown how many duplicates
57+
size: -1, // LOSE: unknown how many duplicates
5858
}
5959
}

pkg/glinq/kv.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ func FromMap[K comparable, V any](m map[K]V) Stream[KeyValue[K, V]] {
2727
keys = append(keys, key)
2828
}
2929

30-
size := len(m)
3130
return &stream[KeyValue[K, V]]{
3231
sourceFactory: func() func() (KeyValue[K, V], bool) {
3332
index := 0 // Fresh index for each iterator
@@ -42,7 +41,7 @@ func FromMap[K comparable, V any](m map[K]V) Stream[KeyValue[K, V]] {
4241
return KeyValue[K, V]{Key: key, Value: value}, true
4342
}
4443
},
45-
size: &size,
44+
size: len(m),
4645
}
4746
}
4847

@@ -66,7 +65,6 @@ func FromMapSafe[K comparable, V any](m map[K]V) Stream[KeyValue[K, V]] {
6665
pairs = append(pairs, KeyValue[K, V]{Key: key, Value: value})
6766
}
6867

69-
size := len(pairs)
7068
return &stream[KeyValue[K, V]]{
7169
sourceFactory: func() func() (KeyValue[K, V], bool) {
7270
index := 0 // Fresh index for each iterator
@@ -79,17 +77,17 @@ func FromMapSafe[K comparable, V any](m map[K]V) Stream[KeyValue[K, V]] {
7977
return result, true
8078
}
8179
},
82-
size: &size,
80+
size: len(pairs),
8381
}
8482
}
8583

8684
// Keys extracts only keys from Enumerable[KeyValue].
8785
// SIZE: Preserves size if source is Sizable (1-to-1 transformation).
8886
func Keys[K comparable, V any](enum Enumerable[KeyValue[K, V]]) Stream[K] {
89-
var size *int
87+
size := -1
9088
if sizable, ok := enum.(Sizable[KeyValue[K, V]]); ok {
9189
if s, known := sizable.Size(); known {
92-
size = &s
90+
size = s
9391
}
9492
}
9593
return &stream[K]{
@@ -110,10 +108,10 @@ func Keys[K comparable, V any](enum Enumerable[KeyValue[K, V]]) Stream[K] {
110108
// Values extracts only values from Enumerable[KeyValue].
111109
// SIZE: Preserves size if source is Sizable (1-to-1 transformation).
112110
func Values[K comparable, V any](enum Enumerable[KeyValue[K, V]]) Stream[V] {
113-
var size *int
111+
size := -1
114112
if sizable, ok := enum.(Sizable[KeyValue[K, V]]); ok {
115113
if s, known := sizable.Size(); known {
116-
size = &s
114+
size = s
117115
}
118116
}
119117
return &stream[V]{
@@ -178,8 +176,7 @@ func GroupBy[T any, K comparable](enum Enumerable[T], keySelector func(T) K) Str
178176
pairs = append(pairs, KeyValue[K, []T]{Key: key, Value: values})
179177
}
180178

181-
// SIZE: Unknown - depends on how many unique keys exist
182-
size := len(pairs)
179+
// SIZE: Known after materialization
183180
return &stream[KeyValue[K, []T]]{
184181
sourceFactory: func() func() (KeyValue[K, []T], bool) {
185182
index := 0 // Fresh index for each iterator
@@ -192,6 +189,6 @@ func GroupBy[T any, K comparable](enum Enumerable[T], keySelector func(T) K) Str
192189
return result, true
193190
}
194191
},
195-
size: &size, // Actually we know it after materialization
192+
size: len(pairs), // Actually we know it after materialization
196193
}
197194
}

pkg/glinq/operators.go

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func (s *stream[T]) Where(predicate func(T) bool) Stream[T] {
2222
}
2323
}
2424
},
25-
size: nil, // LOSE: unknown how many pass filter
25+
size: -1, // LOSE: unknown how many pass filter
2626
}
2727
}
2828

@@ -98,10 +98,10 @@ func (s *stream[T]) SelectWithIndex(mapper func(T, int) T) Stream[T] {
9898
// ).ToSlice()
9999
// // []string{"num_1", "num_2", "num_3"}
100100
func Select[T, R any](enum Enumerable[T], mapper func(T) R) Stream[R] {
101-
var size *int
101+
size := -1
102102
if sizable, ok := enum.(Sizable[T]); ok {
103103
if s, known := sizable.Size(); known {
104-
size = &s
104+
size = s
105105
}
106106
}
107107
return &stream[R]{
@@ -132,10 +132,10 @@ func Select[T, R any](enum Enumerable[T], mapper func(T) R) Stream[R] {
132132
// ).ToSlice()
133133
// // []string{"num_1_at_0", "num_2_at_1", "num_3_at_2"}
134134
func SelectWithIndex[T, R any](enum Enumerable[T], mapper func(T, int) R) Stream[R] {
135-
var size *int
135+
size := -1
136136
if sizable, ok := enum.(Sizable[T]); ok {
137137
if s, known := sizable.Size(); known {
138-
size = &s
138+
size = s
139139
}
140140
}
141141
return &stream[R]{
@@ -160,16 +160,15 @@ func SelectWithIndex[T, R any](enum Enumerable[T], mapper func(T, int) R) Stream
160160
//
161161
// SIZE: Calculated as min(sourceSize, n) if source size known, else n.
162162
func (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
163+
var newSize int
164+
if s.size != -1 {
165+
if s.size < n {
166+
newSize = s.size
168167
} else {
169-
newSize = &n
168+
newSize = n
170169
}
171170
} else {
172-
newSize = &n // Don't know source size, but result won't exceed n
171+
newSize = n // Don't know source size, but result won't exceed n
173172
}
174173

175174
return &stream[T]{
@@ -198,15 +197,15 @@ func (s *stream[T]) Take(n int) Stream[T] {
198197
//
199198
// SIZE: Calculated as max(0, sourceSize - n) if source size known, else unknown.
200199
func (s *stream[T]) Skip(n int) Stream[T] {
201-
var newSize *int
202-
if s.size != nil {
203-
size := *s.size - n
200+
var newSize int = -1
201+
if s.size != -1 {
202+
size := s.size - n
204203
if size < 0 {
205204
size = 0
206205
}
207-
newSize = &size
206+
newSize = size
208207
}
209-
// else: nil (unknown)
208+
// else: -1 (unknown)
210209

211210
return &stream[T]{
212211
sourceFactory: func() func() (T, bool) {
@@ -224,7 +223,7 @@ func (s *stream[T]) Skip(n int) Stream[T] {
224223
return source()
225224
}
226225
},
227-
size: newSize, // CALCULATED: max(0, source - n) or nil
226+
size: newSize, // CALCULATED: max(0, source - n) or -1
228227
}
229228
}
230229

@@ -274,19 +273,19 @@ func TakeOrderedBy[T any](enum Enumerable[T], n int, less func(a, b T) bool) Str
274273
return Empty[T]()
275274
}
276275

277-
var size *int
276+
var size int
278277
if sizable, ok := enum.(Sizable[T]); ok {
279278
if s, known := sizable.Size(); known {
280279
if s < n {
281-
size = &s
280+
size = s
282281
} else {
283-
size = &n
282+
size = n
284283
}
285284
} else {
286-
size = &n // Don't know source size, but result won't exceed n
285+
size = n // Don't know source size, but result won't exceed n
287286
}
288287
} else {
289-
size = &n // Don't know source size, but result won't exceed n
288+
size = n // Don't know source size, but result won't exceed n
290289
}
291290

292291
return &stream[T]{
@@ -413,6 +412,6 @@ func SelectMany[T, R any](enum Enumerable[T], selector func(T) Enumerable[R]) St
413412
}
414413
}
415414
},
416-
size: nil, // LOSE: 1-to-many transformation
415+
size: -1, // LOSE: 1-to-many transformation
417416
}
418417
}

pkg/glinq/ordering.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ func (s *stream[T]) orderBy(ascending bool, comparator func(T, T) int) Stream[T]
1515
})
1616

1717
// SIZE: Preserves size (1-to-1 transformation, materializes)
18-
size := len(sorted)
1918
return &stream[T]{
2019
sourceFactory: func() func() (T, bool) {
2120
index := 0 // Fresh index for each iterator
@@ -29,7 +28,7 @@ func (s *stream[T]) orderBy(ascending bool, comparator func(T, T) int) Stream[T]
2928
return result, true
3029
}
3130
},
32-
size: &size, // PRESERVE: 1-to-1 transformation
31+
size: len(sorted), // PRESERVE: 1-to-1 transformation
3332
}
3433
}
3534

pkg/glinq/set.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ package glinq
55
//
66
// SIZE: Calculated as currentSize + otherSize if both known, else unknown.
77
func (s *stream[T]) Concat(other Enumerable[T]) Stream[T] {
8-
var newSize *int
9-
if s.size != nil {
8+
var newSize int = -1
9+
if s.size != -1 {
1010
if sizable, ok := other.(Sizable[T]); ok {
1111
if otherSize, known := sizable.Size(); known {
12-
size := *s.size + otherSize
13-
newSize = &size
12+
newSize = s.size + otherSize
1413
}
1514
}
1615
}
17-
// else: nil (unknown)
16+
// else: -1 (unknown)
1817

1918
return &stream[T]{
2019
sourceFactory: func() func() (T, bool) {
@@ -79,7 +78,7 @@ func Union[T comparable](e1, e2 Enumerable[T]) Stream[T] {
7978
}
8079
}
8180
},
82-
size: nil, // LOSE: unknown how many duplicates
81+
size: -1, // LOSE: unknown how many duplicates
8382
}
8483
}
8584

@@ -117,7 +116,7 @@ func Intersect[T comparable](e1, e2 Enumerable[T]) Stream[T] {
117116
}
118117
}
119118
},
120-
size: nil, // LOSE: unknown result count
119+
size: -1, // LOSE: unknown result count
121120
}
122121
}
123122

@@ -155,6 +154,6 @@ func Except[T comparable](e1, e2 Enumerable[T]) Stream[T] {
155154
}
156155
}
157156
},
158-
size: nil, // LOSE: unknown result count
157+
size: -1, // LOSE: unknown result count
159158
}
160159
}

pkg/glinq/stream.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ type Stream[T any] interface {
3838
First() (T, bool)
3939
// Count returns the number of elements in Stream.
4040
Count() int
41-
// Any checks if there is at least one element satisfying the predicate.
42-
Any(predicate func(T) bool) bool
41+
// Any checks if there is at least one element in the Stream.
42+
// OPTIMIZATION: Returns O(1) if size is known, otherwise iterates until first element.
43+
Any() bool
44+
// AnyMatch checks if there is at least one element satisfying the predicate.
45+
AnyMatch(predicate func(T) bool) bool
4346
// All checks if all elements satisfy the predicate.
4447
All(predicate func(T) bool) bool
4548
// ForEach executes an action for each element in Stream.
@@ -115,7 +118,7 @@ type Stream[T any] interface {
115118
type stream[T any] struct {
116119
sourceFactory func() func() (T, bool)
117120
currentIterator func() (T, bool) // For Enumerable.Next()
118-
size *int // nil if unknown, pointer to size if known
121+
size int // -1 if unknown, actual size if known
119122
}
120123

121124
// From creates a Stream from a slice.
@@ -132,7 +135,6 @@ type stream[T any] struct {
132135
// stream := From(numbers)
133136
// // Efficient - no copying!
134137
func From[T any](slice []T) Stream[T] {
135-
size := len(slice)
136138
return &stream[T]{
137139
sourceFactory: func() func() (T, bool) {
138140
index := 0 // Fresh index for each iterator
@@ -146,7 +148,7 @@ func From[T any](slice []T) Stream[T] {
146148
return result, true
147149
}
148150
},
149-
size: &size,
151+
size: len(slice),
150152
}
151153
}
152154

@@ -168,7 +170,6 @@ func FromSafe[T any](slice []T) Stream[T] {
168170
data := make([]T, len(slice))
169171
copy(data, slice)
170172

171-
size := len(data)
172173
return &stream[T]{
173174
sourceFactory: func() func() (T, bool) {
174175
index := 0 // Fresh index for each iterator
@@ -182,21 +183,20 @@ func FromSafe[T any](slice []T) Stream[T] {
182183
return result, true
183184
}
184185
},
185-
size: &size,
186+
size: len(data),
186187
}
187188
}
188189

189190
// Empty creates an empty Stream that contains no elements.
190191
func Empty[T any]() Stream[T] {
191-
size := 0
192192
return &stream[T]{
193193
sourceFactory: func() func() (T, bool) {
194194
return func() (T, bool) {
195195
var zero T
196196
return zero, false
197197
}
198198
},
199-
size: &size,
199+
size: 0,
200200
}
201201
}
202202

@@ -214,7 +214,7 @@ func Range(start, count int) Stream[int] {
214214
return result, true
215215
}
216216
},
217-
size: &count,
217+
size: count,
218218
}
219219
}
220220

@@ -228,19 +228,19 @@ func (s *stream[T]) Next() (T, bool) {
228228

229229
// Size implements Sizable
230230
func (s *stream[T]) Size() (int, bool) {
231-
if s.size == nil {
231+
if s.size == -1 {
232232
return 0, false
233233
}
234-
return *s.size, true
234+
return s.size, true
235235
}
236236

237237
// FromEnumerable creates a Stream from any Enumerable.
238238
// SIZE: Preserves size if source is Sizable, otherwise unknown.
239239
func FromEnumerable[T any](enum Enumerable[T]) Stream[T] {
240-
var size *int
240+
size := -1
241241
if sizable, ok := enum.(Sizable[T]); ok {
242242
if s, known := sizable.Size(); known {
243-
size = &s
243+
size = s
244244
}
245245
}
246246
return &stream[T]{

0 commit comments

Comments
 (0)