123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- package help
- import "reflect"
- // GroupJoin correlates the elements of two collections based on key equality,
- // and groups the results.
- //
- // This method produces hierarchical results, which means that elements from
- // outer query are paired with collections of matching elements from inner.
- // GroupJoin enables you to base your results on a whole set of matches for each
- // element of outer query.
- //
- // The resultSelector function is called only one time for each outer element
- // together with a collection of all the inner elements that match the outer
- // element. This differs from the Join method, in which the result selector
- // function is invoked on pairs that contain one element from outer and one
- // element from inner.
- //
- // GroupJoin preserves the order of the elements of outer, and for each element
- // of outer, the order of the matching elements from inner.
- func (q Query) GroupJoin(inner Query,
- outerKeySelector func(interface{}) interface{},
- innerKeySelector func(interface{}) interface{},
- resultSelector func(outer interface{}, inners []interface{}) interface{}) Query {
- return Query{
- Iterate: func() Iterator {
- outernext := q.Iterate()
- innernext := inner.Iterate()
- innerLookup := make(map[interface{}][]interface{})
- for innerItem, ok := innernext(); ok; innerItem, ok = innernext() {
- innerKey := innerKeySelector(innerItem)
- innerLookup[innerKey] = append(innerLookup[innerKey], innerItem)
- }
- return func() (item interface{}, ok bool) {
- if item, ok = outernext(); !ok {
- return
- }
- if group, has := innerLookup[outerKeySelector(item)]; !has {
- item = resultSelector(item, []interface{}{})
- } else {
- item = resultSelector(item, group)
- }
- return
- }
- },
- }
- }
- // GroupJoinT is the typed version of GroupJoin.
- //
- // - inner: The query to join to the outer query.
- // - outerKeySelectorFn is of type "func(TOuter) TKey"
- // - innerKeySelectorFn is of type "func(TInner) TKey"
- // - resultSelectorFn: is of type "func(TOuter, inners []TInner) TResult"
- //
- // NOTE: GroupJoin has better performance than GroupJoinT.
- func (q Query) GroupJoinT(inner Query,
- outerKeySelectorFn interface{},
- innerKeySelectorFn interface{},
- resultSelectorFn interface{}) Query {
- outerKeySelectorGenericFunc, err := newGenericFunc(
- "GroupJoinT", "outerKeySelectorFn", outerKeySelectorFn,
- simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
- )
- if err != nil {
- panic(err)
- }
- outerKeySelectorFunc := func(item interface{}) interface{} {
- return outerKeySelectorGenericFunc.Call(item)
- }
- innerKeySelectorFuncGenericFunc, err := newGenericFunc(
- "GroupJoinT", "innerKeySelectorFn", innerKeySelectorFn,
- simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
- )
- if err != nil {
- panic(err)
- }
- innerKeySelectorFunc := func(item interface{}) interface{} {
- return innerKeySelectorFuncGenericFunc.Call(item)
- }
- resultSelectorGenericFunc, err := newGenericFunc(
- "GroupJoinT", "resultSelectorFn", resultSelectorFn,
- simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
- )
- if err != nil {
- panic(err)
- }
- resultSelectorFunc := func(outer interface{}, inners []interface{}) interface{} {
- innerSliceType := reflect.MakeSlice(resultSelectorGenericFunc.Cache.TypesIn[1], 0, 0)
- innersSlicePointer := reflect.New(innerSliceType.Type())
- From(inners).ToSlice(innersSlicePointer.Interface())
- innersTyped := reflect.Indirect(innersSlicePointer).Interface()
- return resultSelectorGenericFunc.Call(outer, innersTyped)
- }
- return q.GroupJoin(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc)
- }
|