-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjoining.go
More file actions
331 lines (258 loc) · 9.34 KB
/
joining.go
File metadata and controls
331 lines (258 loc) · 9.34 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
package weaklinq
//----------------------------------------------------------------------------//
// Joining //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
// joinType is an enum that represents the type of join to perform.
type joinType int
const (
InnerJoin joinType = iota
LeftJoin
RightJoin
FullOuterJoin
)
////////////////////////////////////////////////////////////////////////////////
// JoinIterable is a specialized iterable that includes a right iterable, a key
// selector for the left iterable, and a key selector for the right iterable.
// These selectors are stored until the collection is iterated, and then
// applied to the items.
type JoinIterable[T any] struct {
itemIterable Iterable[T]
rightIterable Iterable[any]
keySelector func(T) any
rightKeySelector func(any) any
joinType joinType
}
/////////////////////////////////////////////////////////////////////////////////
// DeferredJoinIterable is a JoinIterable where the key selectors have not yet
// been set. Designed to be used in tandem with the On and Equals functions.
// Has very little use outside of that.
type DeferredJoinIterable[T any] JoinIterable[T]
/////////////////////////////////////////////////////////////////////////////////
// Pair is a struct that holds a left and right item for use in join operations.
type Pair[TLeft any, TRight any] struct {
Left TLeft
Right TRight
}
////////////////////////////////////////////////////////////////////////////////
func defaultJoinIterable[T any](iterable Iterable[T], joinIterable Iterable[any]) JoinIterable[T] {
return JoinIterable[T]{
itemIterable: iterable,
rightIterable: joinIterable,
keySelector: identitySelector[T],
rightKeySelector: identitySelector[any],
joinType: InnerJoin,
}
}
////////////////////////////////////////////////////////////////////////////////
// Join returns a new DeferredJoinIterable that will join the items of the given iterable
func (iterable Iterable[T]) Join(joinIterable Iterable[any]) DeferredJoinIterable[T] {
return DeferredJoinIterable[T](defaultJoinIterable(iterable, joinIterable))
/*
linq.From([]T{...}).
Join(linq.From([]TRight{...}).AsAny())
*/
}
////////////////////////////////////////////////////////////////////////////////
// LeftJoin returns a new DeferredJoinIterable that will perform a left join
// on the given iterable.
func (iterable Iterable[T]) LeftJoin(joinIterable Iterable[any]) DeferredJoinIterable[T] {
defaultIterable := defaultJoinIterable(iterable, joinIterable)
defaultIterable.joinType = LeftJoin
return DeferredJoinIterable[T](defaultIterable)
}
////////////////////////////////////////////////////////////////////////////////
// RightJoin returns a new DeferredJoinIterable that will perform a right join
// on the given iterable.
func (iterable Iterable[T]) RightJoin(joinIterable Iterable[any]) DeferredJoinIterable[T] {
defaultIterable := defaultJoinIterable(iterable, joinIterable)
defaultIterable.joinType = RightJoin
return DeferredJoinIterable[T](defaultIterable)
}
////////////////////////////////////////////////////////////////////////////////
// FullOuterJoin returns a new DeferredJoinIterable that will perform a full
// outer join on the given iterable.
func (iterable Iterable[T]) FullOuterJoin(joinIterable Iterable[any]) DeferredJoinIterable[T] {
defaultIterable := defaultJoinIterable(iterable, joinIterable)
defaultIterable.joinType = FullOuterJoin
return DeferredJoinIterable[T](defaultIterable)
}
////////////////////////////////////////////////////////////////////////////////
// JoinSlice returns a new DeferredJoinIterable that will join the items of the given slice
func (iterable Iterable[T]) JoinSlice(joinSlice []T) DeferredJoinIterable[T] {
return iterable.Join(From(joinSlice).AsAny())
/*
linq.From([]T{...}).
JoinSlice([]TRight{...})
*/
}
////////////////////////////////////////////////////////////////////////////////
// LeftJoinSlice returns a new DeferredJoinIterable that will perform a left
// join on the given slice.
func (iterable Iterable[T]) LeftJoinSlice(joinSlice []T) DeferredJoinIterable[T] {
return iterable.LeftJoin(From(joinSlice).AsAny())
/*
linq.From([]T{...}).
LeftJoinSlice([]TRight{...})
*/
}
// RightJoinSlice returns a new DeferredJoinIterable that will perform a right
// join on the given slice.
func (iterable Iterable[T]) RightJoinSlice(joinSlice []T) DeferredJoinIterable[T] {
return iterable.RightJoin(From(joinSlice).AsAny())
/*
linq.From([]T{...}).
RightJoinSlice([]TRight{...})
*/
}
////////////////////////////////////////////////////////////////////////////////
// FullOuterJoinSlice returns a new DeferredJoinIterable that will perform a
// full outer join on the given slice.
func (iterable Iterable[T]) FullOuterJoinSlice(joinSlice []T) DeferredJoinIterable[T] {
return iterable.FullOuterJoin(From(joinSlice).AsAny())
/*
linq.From([]T{...}).
FullOuterJoinSlice([]TRight{...})
*/
}
////////////////////////////////////////////////////////////////////////////////
// OnThis sets the key selector functions for both left and right iterables.
func (iterable DeferredJoinIterable[T]) OnThis(keySelector func(T) any) DeferredJoinIterable[T] {
iterable.keySelector = keySelector
iterable.rightKeySelector = func(item any) any {
return keySelector(item.(T))
}
return iterable
/*
linq.From([]T{...}).
Join(linq.From([]TRight{...}).AsAny()).
OnThis(
func(left T) any {
return ...
},
)
*/
}
////////////////////////////////////////////////////////////////////////////////
// On sets the key selector functions for both left and right iterables using
// the given field name.
func (iterable DeferredJoinIterable[T]) On(fieldName string) DeferredJoinIterable[T] {
return iterable.OnThis(
getFieldNameFunc[T](fieldName),
)
/*
linq.From([]T{...}).
Join(linq.From([]TRight{...}).AsAny()).
On("KeyField")
*/
}
////////////////////////////////////////////////////////////////////////////////
// EqualsThis sets the right key selector function.
func (iterable DeferredJoinIterable[T]) EqualsThis(rightKeySelector func(any) any) DeferredJoinIterable[T] {
iterable.rightKeySelector = rightKeySelector
return iterable
/*
linq.From([]T{...}).
Join(linq.From([]TRight{...}).AsAny()).
On("LeftKeyField").
EqualsThis(
func(right any) any {
return ...
},
)
*/
}
////////////////////////////////////////////////////////////////////////////////
// Equals sets the right key selector function using the given field name.
func (iterable DeferredJoinIterable[T]) Equals(fieldName string) DeferredJoinIterable[T] {
return iterable.EqualsThis(
getFieldNameFunc[any](fieldName),
)
/*
linq.From([]T{...}).
Join(linq.From([]TRight{...}).AsAny()).
On("LeftKeyField").
Equals("RightKeyField")
*/
}
////////////////////////////////////////////////////////////////////////////////
// AsThis projects the joined items using the given joinSelector function.
func (iterable DeferredJoinIterable[T]) AsThis(joinSelector func(T, any) any) Iterable[any] {
rightKeysToRightItems := make(map[any][]any)
for rightItem := range iterable.rightIterable.Seq {
rightKey := iterable.rightKeySelector(rightItem)
rightKeysToRightItems[rightKey] = append(rightKeysToRightItems[rightKey], rightItem)
}
return Iterable[any]{
Seq: func(yield func(any) bool) {
matchedRightItems := make(map[any]bool)
// Process left items
iterable.itemIterable.Seq(func(leftItem T) bool {
leftKey := iterable.keySelector(leftItem)
rightItems, hasMatch := rightKeysToRightItems[leftKey]
if hasMatch {
// Inner, Left, Right, or Full Join with matches
for _, rightItem := range rightItems {
matchedRightItems[rightItem] = true
result := joinSelector(leftItem, rightItem)
if !yield(result) {
return false
}
}
} else if iterable.joinType == LeftJoin || iterable.joinType == FullOuterJoin {
// Left or Full Join with no match - yield left with nil right
result := joinSelector(leftItem, nil)
if !yield(result) {
return false
}
}
return true
})
// Handle unmatched right items for Right and Full Join
if iterable.joinType == RightJoin || iterable.joinType == FullOuterJoin {
for _, rightItems := range rightKeysToRightItems {
for _, rightItem := range rightItems {
if !matchedRightItems[rightItem] {
// Yield nil left with unmatched right
var zeroLeft T
result := joinSelector(zeroLeft, rightItem)
if !yield(result) {
return
}
}
}
}
}
},
}
/*
linq.From([]T{...}).
Join(linq.From([]TRight{...}).AsAny()).
On("LeftKeyField").
Equals("RightKeyField").
AsThis(
func(left T, right any) any {
return ...
},
)
*/
}
////////////////////////////////////////////////////////////////////////////////
// AsPairs projects the joined items as Pair structs.
func (iterable DeferredJoinIterable[T]) AsPairs() Iterable[any] {
return iterable.AsThis(
func(left T, right any) any {
return Pair[T, any]{
Left: left,
Right: right,
}
},
)
/*
linq.From([]T{...}).
Join(linq.From([]TRight{...}).AsAny()).
On("LeftKeyField").
Equals("RightKeyField").
AsPairs()
*/
}