<commit_before>// 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

// MapTables applies f to each Table in g and returns a new Grouping
// with the same group structure as g, but with the Tables returned by
// f.
func MapTables(f func(gid GroupID, table *Table) *Table, g Grouping) Grouping {
	var out GroupingBuilder
	for _, gid := range g.Tables() {
		out.Add(gid, f(gid, g.Table(gid)))
	}
	return out.Done()
}<commit_msg>table: add MapCols for constructing new columns using a function<commit_after>// 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 (
	"fmt"
	"reflect"

	"github.com/aclements/go-gg/generic"
)

// MapTables applies f to each Table in g and returns a new Grouping
// with the same group structure as g, but with the Tables returned by
// f.
func MapTables(f func(gid GroupID, table *Table) *Table, g Grouping) Grouping {
	var out GroupingBuilder
	for _, gid := range g.Tables() {
		out.Add(gid, f(gid, g.Table(gid)))
	}
	return out.Done()
}

// MapCols applies f to a set of input columns to construct a set of
// new output columns.
//
// For each Table in g, MapCols calls f(in[0], in[1], ..., out[0],
// out[1], ...) where in[i] is column incols[i]. f should process the
// values in the input column slices and fill output columns slices
// out[j] accordingly. MapCols returns a new Grouping that adds each
// outcols[j] bound to out[j].
func MapCols(f interface{}, g Grouping, incols ...string) func(outcols ...string) Grouping {
	return func(outcols ...string) Grouping {
		fv := reflect.ValueOf(f)
		if fv.Kind() != reflect.Func {
			panic(&generic.TypeError{fv.Type(), nil, "must be a function"})
		}
		ft := fv.Type()
		if ft.NumIn() != len(incols)+len(outcols) {
			panic(&generic.TypeError{ft, nil, fmt.Sprintf("has the wrong number of arguments; expected %d", len(incols)+len(outcols))})
		}
		if ft.NumOut() != 0 {
			panic(&generic.TypeError{ft, nil, "has the wrong number of results; expected 0"})
		}

		// Create output column slices.
		totalRows := 0
		for _, gid := range g.Tables() {
			totalRows += g.Table(gid).Len()
		}
		ocols := make([]reflect.Value, len(outcols))
		for i := range ocols {
			ocols[i] = reflect.MakeSlice(ft.In(i+len(incols)), totalRows, totalRows)
		}

		// Apply f to each group.
		var out GroupingBuilder
		args := make([]reflect.Value, len(incols)+len(outcols))
		opos := 0
		for _, gid := range g.Tables() {
			t := g.Table(gid)

			// Prepare arguments.
			for i, incol := range incols {
				args[i] = reflect.ValueOf(t.MustColumn(incol))
			}
			for i, ocol := range ocols {
				args[i+len(incols)] = ocol.Slice(opos, opos+t.Len())
			}
			opos += t.Len()

			// Call f.
			fv.Call(args)

			// Add output columns.
			tb := NewBuilder(t)
			for i, outcol := range outcols {
				tb.Add(outcol, args[i+len(incols)].Interface())
			}
			out.Add(gid, tb.Done())
		}
		return out.Done()
	}
}
{'fraction_non_alphanumeric': 0.14023801865551624, 'fraction_numerical': 0.005146349308459312, 'mean_word_length': 3.3134535367545075, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5746521', 'n_tokens_mistral': 1157, 'n_tokens_neox': 1038, 'n_words': 367}