Skip to content

Commit bd15765

Browse files
committed
Merge pull request #12 from mkitawaki/myFeatureSpike
Add SelectMany and SelectManyBy
2 parents 8abc193 + e0d717a commit bd15765

File tree

4 files changed

+389
-0
lines changed

4 files changed

+389
-0
lines changed

linq.go

+58
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,64 @@ func (q Query) Select(f func(T) (T, error)) (r Query) {
168168
return
169169
}
170170

171+
// SelectMany returns flattens the resulting sequences into one sequence.
172+
//
173+
// Example:
174+
// names, err := From(parents).SelectMany(func (p T, idx int) (T, error) {
175+
// return p.(*Parent).Children, nil
176+
// }).Results()
177+
func (q Query) SelectMany(f func(T, int) (T, error)) (r Query) {
178+
return q.SelectManyBy(f, func(p T, c T) (T, error) {
179+
return c, nil
180+
})
181+
}
182+
183+
// SelectMany returns flattens the resulting sequences into one sequence.
184+
//
185+
// resultSelector takes parent element and child element as inputs
186+
// and returns a value which will be an element in the resulting query.
187+
//
188+
// Example:
189+
// names, err := From(parents).SelectManyBy(func (p T, idx int) (T, error) {
190+
// return p.(*Parent).Children, nil
191+
// }, func (p T, c T) (T, error) {
192+
// return p.(*Parent).Name + ":" + c.(*Child).Name, nil
193+
// }).Results()
194+
func (q Query) SelectManyBy(f func(T, int) (T, error),
195+
resultSelector func(T, T) (T, error)) (r Query) {
196+
197+
if q.err != nil {
198+
r.err = q.err
199+
return r
200+
}
201+
if f == nil || resultSelector == nil {
202+
r.err = ErrNilFunc
203+
return
204+
}
205+
206+
for i, p := range q.values {
207+
val, err := f(p, i)
208+
if err != nil {
209+
r.err = err
210+
return r
211+
}
212+
innerCollection, ok := takeSliceArg(val)
213+
if !ok {
214+
r.err = ErrInvalidInput
215+
return r
216+
}
217+
for _, c := range innerCollection {
218+
res, err := resultSelector(p, c)
219+
if err != nil {
220+
r.err = err
221+
return r
222+
}
223+
r.values = append(r.values, res)
224+
}
225+
}
226+
return
227+
}
228+
171229
// Distinct returns distinct elements from the provided source using default
172230
// equality comparer, ==. This is a set operation and returns an unordered
173231
// sequence.

linq_test.go

+132
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,138 @@ func TestSelect(t *testing.T) {
226226
})
227227
}
228228

229+
type bar struct {
230+
str string
231+
foos []foo
232+
}
233+
type fooBar struct {
234+
fooStr string
235+
barStr string
236+
}
237+
238+
var (
239+
fooArr = []foo{foo{"A", 0}, foo{"B", 1}, foo{"C", -1}}
240+
barArr = []bar{bar{"a", []foo{foo{"A", 0}, foo{"B", 1}}}, bar{"b", []foo{foo{"C", -1}}}}
241+
fooEmpty = []bar{bar{"c", nil}}
242+
fooBarArr = []fooBar{fooBar{"A", "a"}, fooBar{"B", "a"}, fooBar{"C", "b"}}
243+
)
244+
245+
func TestSelectMany(t *testing.T) {
246+
children := func(i T, x int) (T, error) {
247+
return i.(bar).foos, nil
248+
}
249+
erroneusFunc := func(i T, x int) (T, error) {
250+
return nil, errFoo
251+
}
252+
253+
c.Convey("Previous error is reflected on result", t, func() {
254+
_, err := From(barArr).Where(erroneusBinaryFunc).SelectMany(children).Results()
255+
c.So(err, c.ShouldNotEqual, nil)
256+
})
257+
258+
c.Convey("Nil func returns error", t, func() {
259+
_, err := From(barArr).SelectMany(nil).Results()
260+
c.So(err, c.ShouldEqual, ErrNilFunc)
261+
})
262+
263+
c.Convey("Error returned from provided func", t, func() {
264+
val, err := From(barArr).SelectMany(erroneusFunc).Results()
265+
c.So(err, c.ShouldNotEqual, nil)
266+
267+
c.Convey("Erroneus function is in chain with as-is select", func() {
268+
_, err = From(barArr).SelectMany(children).SelectMany(erroneusFunc).Results()
269+
c.So(err, c.ShouldNotEqual, nil)
270+
})
271+
c.Convey("Erroneus function is in chain but not called", func() {
272+
val, err = From(barArr).Where(alwaysFalse).SelectMany(erroneusFunc).Results()
273+
c.So(err, c.ShouldEqual, nil)
274+
c.So(len(val), c.ShouldEqual, 0)
275+
})
276+
277+
})
278+
279+
c.Convey("Select empty as is", t, func() {
280+
val, err := From(fooEmpty).SelectMany(children).Results()
281+
c.So(err, c.ShouldEqual, nil)
282+
c.So(val, shouldSlicesResemble, empty)
283+
})
284+
285+
c.Convey("Select all elements as is", t, func() {
286+
val, err := From(barArr).SelectMany(children).Results()
287+
c.So(err, c.ShouldEqual, nil)
288+
c.So(val, shouldSlicesResemble, fooArr)
289+
})
290+
}
291+
292+
func TestSelectManyBy(t *testing.T) {
293+
children := func(b T, x int) (T, error) {
294+
return b.(bar).foos, nil
295+
}
296+
barStr := func(b T, f T) (T, error) {
297+
return fooBar{f.(foo).str, b.(bar).str}, nil
298+
}
299+
erroneusFunc := func(b T, x int) (T, error) {
300+
return nil, errFoo
301+
}
302+
erroneusSelectFunc := func(b T, f T) (T, error) {
303+
return nil, errFoo
304+
}
305+
306+
c.Convey("Previous error is reflected on result", t, func() {
307+
_, err := From(barArr).Where(erroneusBinaryFunc).SelectManyBy(children, barStr).Results()
308+
c.So(err, c.ShouldNotEqual, nil)
309+
})
310+
311+
c.Convey("Nil transform func returns error", t, func() {
312+
_, err := From(barArr).SelectManyBy(nil, barStr).Results()
313+
c.So(err, c.ShouldEqual, ErrNilFunc)
314+
})
315+
316+
c.Convey("Nil resultSelect func returns error", t, func() {
317+
_, err := From(barArr).SelectManyBy(children, nil).Results()
318+
c.So(err, c.ShouldEqual, ErrNilFunc)
319+
})
320+
321+
c.Convey("Both nil func returns error", t, func() {
322+
_, err := From(barArr).SelectManyBy(nil, nil).Results()
323+
c.So(err, c.ShouldEqual, ErrNilFunc)
324+
})
325+
326+
c.Convey("Error returned from provided func", t, func() {
327+
val, err := From(barArr).SelectManyBy(erroneusFunc, barStr).Results()
328+
c.So(err, c.ShouldNotEqual, nil)
329+
330+
val, err = From(barArr).SelectManyBy(children, erroneusSelectFunc).Results()
331+
c.So(err, c.ShouldNotEqual, nil)
332+
333+
val, err = From(barArr).SelectManyBy(erroneusFunc, erroneusSelectFunc).Results()
334+
c.So(err, c.ShouldNotEqual, nil)
335+
336+
c.Convey("Erroneus function is in chain with as-is select", func() {
337+
_, err = From(barArr).SelectManyBy(children, barStr).SelectManyBy(erroneusFunc, erroneusSelectFunc).Results()
338+
c.So(err, c.ShouldNotEqual, nil)
339+
})
340+
c.Convey("Erroneus function is in chain but not called", func() {
341+
val, err = From(barArr).Where(alwaysFalse).SelectManyBy(erroneusFunc, erroneusSelectFunc).Results()
342+
c.So(err, c.ShouldEqual, nil)
343+
c.So(len(val), c.ShouldEqual, 0)
344+
})
345+
346+
})
347+
348+
c.Convey("Select empty as is", t, func() {
349+
val, err := From(fooEmpty).SelectManyBy(children, barStr).Results()
350+
c.So(err, c.ShouldEqual, nil)
351+
c.So(val, shouldSlicesResemble, empty)
352+
})
353+
354+
c.Convey("Select all elements as is", t, func() {
355+
val, err := From(barArr).SelectManyBy(children, barStr).Results()
356+
c.So(err, c.ShouldEqual, nil)
357+
c.So(val, shouldSlicesResemble, fooBarArr)
358+
})
359+
}
360+
229361
func TestDistinct(t *testing.T) {
230362
c.Convey("Empty slice", t, func() {
231363
res, err := From(empty).Distinct().Results()

plinq.go

+83
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ type parallelValueResult struct {
2121
index int
2222
}
2323

24+
type parallelArrayValueResult struct {
25+
values []T
26+
err error
27+
index int
28+
}
29+
2430
// Results evaluates the query and returns the results as T slice.
2531
// An error occurred in during evaluation of the query will be returned.
2632
//
@@ -198,6 +204,83 @@ func (q ParallelQuery) Select(f func(T) (T, error)) (r ParallelQuery) {
198204
return
199205
}
200206

207+
// SelectMany returns flattens the resulting sequences into one sequence.
208+
//
209+
// Example:
210+
// names, err := From(parents).AsParallel().SelectMany(func (p T, idx int) (T, error) {
211+
// return p.(*Parent).Children, nil
212+
// }).Results()
213+
func (q ParallelQuery) SelectMany(f func(T, int) (T, error)) (r ParallelQuery) {
214+
return q.SelectManyBy(f, func(p T, c T) (T, error) {
215+
return c, nil
216+
})
217+
}
218+
219+
// SelectMany returns flattens the resulting sequences into one sequence.
220+
//
221+
// resultSelector takes parent element and child element as inputs
222+
// and returns a value which will be an element in the resulting query.
223+
//
224+
// Example:
225+
// names, err := From(parents).AsParallel().SelectManyBy(func (p T, idx int) (T, error) {
226+
// return p.(*Parent).Children, nil
227+
// }, func (p T, c T) (T, error) {
228+
// return p.(*Parent).Name + ":" + c.(*Child).Name, nil
229+
// }).Results()
230+
func (q ParallelQuery) SelectManyBy(f func(T, int) (T, error),
231+
resultSelector func(T, T) (T, error)) (r ParallelQuery) {
232+
233+
r = q.copyMeta()
234+
if r.err != nil {
235+
return r
236+
}
237+
if f == nil || resultSelector == nil {
238+
r.err = ErrNilFunc
239+
return
240+
}
241+
242+
ch := make(chan *parallelArrayValueResult)
243+
arrValues := make([][]T, len(q.values))
244+
for i, v := range q.values {
245+
go func(ind int, f func(T, int) (T, error), in T) {
246+
out := parallelArrayValueResult{index: ind}
247+
val, err := f(in, ind)
248+
if err != nil {
249+
out.err = err
250+
} else {
251+
innerCollection, ok := takeSliceArg(val)
252+
if !ok {
253+
out.err = ErrInvalidInput
254+
}
255+
for _, v := range innerCollection {
256+
res, err := resultSelector(in, v)
257+
if err != nil {
258+
out.err = err
259+
} else {
260+
out.values = append(out.values, res)
261+
}
262+
}
263+
}
264+
ch <- &out
265+
}(i, f, v)
266+
}
267+
268+
for i := 0; i < len(q.values); i++ {
269+
out := <-ch
270+
if out.err != nil {
271+
r.err = out.err
272+
return
273+
}
274+
arrValues[out.index] = out.values
275+
}
276+
for _, arr := range arrValues {
277+
for _, v := range arr {
278+
r.values = append(r.values, v)
279+
}
280+
}
281+
return
282+
}
283+
201284
// Any determines whether the query source contains any elements.
202285
// Example:
203286
// anyOver18, err := From(students).AsParallel().Where(func (s T)(bool, error){

0 commit comments

Comments
 (0)