blob: 03ce51b9bac2d1cb73a1bfc3c034e28d8579bbe5 [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package table
import (
"reflect"
"github.com/aclements/go-gg/generic/slice"
)
// Join joins g1 and g2 on tables with identical group IDs where col1
// in g1 equals col2 in g2. It maintains the group order of g1, except
// that groups that aren't in g2 are removed, and maintains the row
// order of g1, followed by the row order of g2.
//
// TODO: Support join on more than one column.
func Join(g1 Grouping, col1 string, g2 Grouping, col2 string) Grouping {
var ng GroupingBuilder
for _, gid := range g1.Tables() {
t1, t2 := g1.Table(gid), g2.Table(gid)
if t2 == nil {
continue
}
// TODO: Optimize for cases where col1 and/or col2 are
// constant.
// Index col2 in t2.
ridx := make(map[interface{}][]int)
rv := reflect.ValueOf(t2.MustColumn(col2))
for i, l := 0, rv.Len(); i < l; i++ {
v := rv.Index(i).Interface()
ridx[v] = append(ridx[v], i)
}
// For each row in t1, find the matching rows in col2
// and build up the row indexes for t1 and t2.
idx1, idx2 := []int{}, []int{}
lv := reflect.ValueOf(t1.MustColumn(col1))
for i, l := 0, lv.Len(); i < l; i++ {
r := ridx[lv.Index(i).Interface()]
for range r {
idx1 = append(idx1, i)
}
idx2 = append(idx2, r...)
}
// Build the joined table.
var nt Builder
for _, col := range t1.Columns() {
if cv, ok := t1.Const(col); ok {
nt.Add(col, cv)
continue
}
nt.Add(col, slice.Select(t1.Column(col), idx1))
}
for _, col := range t2.Columns() {
// Often the join column is the same in both
// and we can skip it because we added it from
// the first table.
if col == col1 && col == col2 {
continue
}
if cv, ok := t2.Const(col); ok {
nt.Add(col, cv)
continue
}
nt.Add(col, slice.Select(t2.Column(col), idx2))
}
ng.Add(gid, nt.Done())
}
return ng.Done()
}