groupjoin.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package help
  2. import "reflect"
  3. // GroupJoin correlates the elements of two collections based on key equality,
  4. // and groups the results.
  5. //
  6. // This method produces hierarchical results, which means that elements from
  7. // outer query are paired with collections of matching elements from inner.
  8. // GroupJoin enables you to base your results on a whole set of matches for each
  9. // element of outer query.
  10. //
  11. // The resultSelector function is called only one time for each outer element
  12. // together with a collection of all the inner elements that match the outer
  13. // element. This differs from the Join method, in which the result selector
  14. // function is invoked on pairs that contain one element from outer and one
  15. // element from inner.
  16. //
  17. // GroupJoin preserves the order of the elements of outer, and for each element
  18. // of outer, the order of the matching elements from inner.
  19. func (q Query) GroupJoin(inner Query,
  20. outerKeySelector func(interface{}) interface{},
  21. innerKeySelector func(interface{}) interface{},
  22. resultSelector func(outer interface{}, inners []interface{}) interface{}) Query {
  23. return Query{
  24. Iterate: func() Iterator {
  25. outernext := q.Iterate()
  26. innernext := inner.Iterate()
  27. innerLookup := make(map[interface{}][]interface{})
  28. for innerItem, ok := innernext(); ok; innerItem, ok = innernext() {
  29. innerKey := innerKeySelector(innerItem)
  30. innerLookup[innerKey] = append(innerLookup[innerKey], innerItem)
  31. }
  32. return func() (item interface{}, ok bool) {
  33. if item, ok = outernext(); !ok {
  34. return
  35. }
  36. if group, has := innerLookup[outerKeySelector(item)]; !has {
  37. item = resultSelector(item, []interface{}{})
  38. } else {
  39. item = resultSelector(item, group)
  40. }
  41. return
  42. }
  43. },
  44. }
  45. }
  46. // GroupJoinT is the typed version of GroupJoin.
  47. //
  48. // - inner: The query to join to the outer query.
  49. // - outerKeySelectorFn is of type "func(TOuter) TKey"
  50. // - innerKeySelectorFn is of type "func(TInner) TKey"
  51. // - resultSelectorFn: is of type "func(TOuter, inners []TInner) TResult"
  52. //
  53. // NOTE: GroupJoin has better performance than GroupJoinT.
  54. func (q Query) GroupJoinT(inner Query,
  55. outerKeySelectorFn interface{},
  56. innerKeySelectorFn interface{},
  57. resultSelectorFn interface{}) Query {
  58. outerKeySelectorGenericFunc, err := newGenericFunc(
  59. "GroupJoinT", "outerKeySelectorFn", outerKeySelectorFn,
  60. simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
  61. )
  62. if err != nil {
  63. panic(err)
  64. }
  65. outerKeySelectorFunc := func(item interface{}) interface{} {
  66. return outerKeySelectorGenericFunc.Call(item)
  67. }
  68. innerKeySelectorFuncGenericFunc, err := newGenericFunc(
  69. "GroupJoinT", "innerKeySelectorFn", innerKeySelectorFn,
  70. simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
  71. )
  72. if err != nil {
  73. panic(err)
  74. }
  75. innerKeySelectorFunc := func(item interface{}) interface{} {
  76. return innerKeySelectorFuncGenericFunc.Call(item)
  77. }
  78. resultSelectorGenericFunc, err := newGenericFunc(
  79. "GroupJoinT", "resultSelectorFn", resultSelectorFn,
  80. simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
  81. )
  82. if err != nil {
  83. panic(err)
  84. }
  85. resultSelectorFunc := func(outer interface{}, inners []interface{}) interface{} {
  86. innerSliceType := reflect.MakeSlice(resultSelectorGenericFunc.Cache.TypesIn[1], 0, 0)
  87. innersSlicePointer := reflect.New(innerSliceType.Type())
  88. From(inners).ToSlice(innersSlicePointer.Interface())
  89. innersTyped := reflect.Indirect(innersSlicePointer).Interface()
  90. return resultSelectorGenericFunc.Call(outer, innersTyped)
  91. }
  92. return q.GroupJoin(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc)
  93. }