join.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package help
  2. // Join correlates the elements of two collection based on matching keys.
  3. //
  4. // A join refers to the operation of correlating the elements of two sources of
  5. // information based on a common key. Join brings the two information sources
  6. // and the keys by which they are matched together in one method call. This
  7. // differs from the use of SelectMany, which requires more than one method call
  8. // to perform the same operation.
  9. //
  10. // Join preserves the order of the elements of outer collection, and for each of
  11. // these elements, the order of the matching elements of inner.
  12. func (q Query) Join(inner Query,
  13. outerKeySelector func(interface{}) interface{},
  14. innerKeySelector func(interface{}) interface{},
  15. resultSelector func(outer interface{}, inner interface{}) interface{}) Query {
  16. return Query{
  17. Iterate: func() Iterator {
  18. outernext := q.Iterate()
  19. innernext := inner.Iterate()
  20. innerLookup := make(map[interface{}][]interface{})
  21. for innerItem, ok := innernext(); ok; innerItem, ok = innernext() {
  22. innerKey := innerKeySelector(innerItem)
  23. innerLookup[innerKey] = append(innerLookup[innerKey], innerItem)
  24. }
  25. var outerItem interface{}
  26. var innerGroup []interface{}
  27. innerLen, innerIndex := 0, 0
  28. return func() (item interface{}, ok bool) {
  29. if innerIndex >= innerLen {
  30. has := false
  31. for !has {
  32. outerItem, ok = outernext()
  33. if !ok {
  34. return
  35. }
  36. innerGroup, has = innerLookup[outerKeySelector(outerItem)]
  37. innerLen = len(innerGroup)
  38. innerIndex = 0
  39. }
  40. }
  41. item = resultSelector(outerItem, innerGroup[innerIndex])
  42. innerIndex++
  43. return item, true
  44. }
  45. },
  46. }
  47. }
  48. // JoinT is the typed version of Join.
  49. //
  50. // - outerKeySelectorFn is of type "func(TOuter) TKey"
  51. // - innerKeySelectorFn is of type "func(TInner) TKey"
  52. // - resultSelectorFn is of type "func(TOuter,TInner) TResult"
  53. //
  54. // NOTE: Join has better performance than JoinT.
  55. func (q Query) JoinT(inner Query,
  56. outerKeySelectorFn interface{},
  57. innerKeySelectorFn interface{},
  58. resultSelectorFn interface{}) Query {
  59. outerKeySelectorGenericFunc, err := newGenericFunc(
  60. "JoinT", "outerKeySelectorFn", outerKeySelectorFn,
  61. simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
  62. )
  63. if err != nil {
  64. panic(err)
  65. }
  66. outerKeySelectorFunc := func(item interface{}) interface{} {
  67. return outerKeySelectorGenericFunc.Call(item)
  68. }
  69. innerKeySelectorFuncGenericFunc, err := newGenericFunc(
  70. "JoinT", "innerKeySelectorFn",
  71. innerKeySelectorFn,
  72. simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
  73. )
  74. if err != nil {
  75. panic(err)
  76. }
  77. innerKeySelectorFunc := func(item interface{}) interface{} {
  78. return innerKeySelectorFuncGenericFunc.Call(item)
  79. }
  80. resultSelectorGenericFunc, err := newGenericFunc(
  81. "JoinT", "resultSelectorFn", resultSelectorFn,
  82. simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
  83. )
  84. if err != nil {
  85. panic(err)
  86. }
  87. resultSelectorFunc := func(outer interface{}, inner interface{}) interface{} {
  88. return resultSelectorGenericFunc.Call(outer, inner)
  89. }
  90. return q.Join(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc)
  91. }