mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-30 03:01:39 +00:00
178 lines
4 KiB
Go
178 lines
4 KiB
Go
package transformers
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"miller/src/cliutil"
|
|
"miller/src/lib"
|
|
"miller/src/transforming"
|
|
"miller/src/types"
|
|
)
|
|
|
|
// ----------------------------------------------------------------
|
|
const verbNameHead = "head"
|
|
|
|
var HeadSetup = transforming.TransformerSetup{
|
|
Verb: verbNameHead,
|
|
UsageFunc: transformerHeadUsage,
|
|
ParseCLIFunc: transformerHeadParseCLI,
|
|
IgnoresInput: false,
|
|
}
|
|
|
|
func transformerHeadUsage(
|
|
o *os.File,
|
|
doExit bool,
|
|
exitCode int,
|
|
) {
|
|
fmt.Fprintf(o, "Usage: %s %s [options]\n", lib.MlrExeName(), verbNameHead)
|
|
fmt.Fprintf(o, "Passes through the first n records, optionally by category.\n")
|
|
|
|
fmt.Fprintf(o, "Options:\n")
|
|
fmt.Fprintf(o, "-g {a,b,c} Optional group-by-field names for head counts, e.g. a,b,c.\n")
|
|
fmt.Fprintf(o, "-n {n} Head-count to print. Default 10.\n")
|
|
fmt.Fprintf(o, "-h|--help Show this message.\n")
|
|
|
|
// TODO: work on this, keeping in mind https://github.com/johnkerl/miller/issues/291
|
|
// fmt.Fprint(o,
|
|
// `Without -g, ceases consuming more input (i.e. is fast) when n records
|
|
//have been read.
|
|
//`)
|
|
|
|
if doExit {
|
|
os.Exit(exitCode)
|
|
}
|
|
}
|
|
|
|
func transformerHeadParseCLI(
|
|
pargi *int,
|
|
argc int,
|
|
args []string,
|
|
_ *cliutil.TReaderOptions,
|
|
__ *cliutil.TWriterOptions,
|
|
) transforming.IRecordTransformer {
|
|
|
|
// Skip the verb name from the current spot in the mlr command line
|
|
argi := *pargi
|
|
verb := args[argi]
|
|
argi++
|
|
|
|
headCount := 10
|
|
var groupByFieldNames []string = nil
|
|
|
|
for argi < argc /* variable increment: 1 or 2 depending on flag */ {
|
|
opt := args[argi]
|
|
if !strings.HasPrefix(opt, "-") {
|
|
break // No more flag options to process
|
|
}
|
|
argi++
|
|
|
|
if opt == "-h" || opt == "--help" {
|
|
transformerHeadUsage(os.Stdout, true, 0)
|
|
|
|
} else if opt == "-n" {
|
|
headCount = cliutil.VerbGetIntArgOrDie(verb, opt, args, &argi, argc)
|
|
|
|
} else if opt == "-g" {
|
|
groupByFieldNames = cliutil.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
|
|
|
|
} else {
|
|
transformerHeadUsage(os.Stderr, true, 1)
|
|
}
|
|
}
|
|
|
|
transformer, _ := NewTransformerHead(
|
|
headCount,
|
|
groupByFieldNames,
|
|
)
|
|
|
|
*pargi = argi
|
|
return transformer
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
type TransformerHead struct {
|
|
// input
|
|
headCount int
|
|
groupByFieldNames []string
|
|
|
|
// state
|
|
recordTransformerFunc transforming.RecordTransformerFunc
|
|
unkeyedRecordCount int
|
|
keyedRecordCounts map[string]int
|
|
}
|
|
|
|
func NewTransformerHead(
|
|
headCount int,
|
|
groupByFieldNames []string,
|
|
) (*TransformerHead, error) {
|
|
|
|
this := &TransformerHead{
|
|
headCount: headCount,
|
|
groupByFieldNames: groupByFieldNames,
|
|
|
|
unkeyedRecordCount: 0,
|
|
keyedRecordCounts: make(map[string]int),
|
|
}
|
|
|
|
if groupByFieldNames == nil {
|
|
this.recordTransformerFunc = this.mapUnkeyed
|
|
} else {
|
|
this.recordTransformerFunc = this.mapKeyed
|
|
}
|
|
|
|
return this, nil
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
func (this *TransformerHead) Transform(
|
|
inrecAndContext *types.RecordAndContext,
|
|
outputChannel chan<- *types.RecordAndContext,
|
|
) {
|
|
this.recordTransformerFunc(inrecAndContext, outputChannel)
|
|
}
|
|
|
|
func (this *TransformerHead) mapUnkeyed(
|
|
inrecAndContext *types.RecordAndContext,
|
|
outputChannel chan<- *types.RecordAndContext,
|
|
) {
|
|
if !inrecAndContext.EndOfStream {
|
|
this.unkeyedRecordCount++
|
|
if this.unkeyedRecordCount <= this.headCount {
|
|
outputChannel <- inrecAndContext
|
|
}
|
|
} else {
|
|
outputChannel <- inrecAndContext
|
|
}
|
|
}
|
|
|
|
func (this *TransformerHead) mapKeyed(
|
|
inrecAndContext *types.RecordAndContext,
|
|
outputChannel chan<- *types.RecordAndContext,
|
|
) {
|
|
if !inrecAndContext.EndOfStream {
|
|
inrec := inrecAndContext.Record
|
|
|
|
groupingKey, ok := inrec.GetSelectedValuesJoined(this.groupByFieldNames)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
count, present := this.keyedRecordCounts[groupingKey]
|
|
if !present { // first time
|
|
this.keyedRecordCounts[groupingKey] = 1
|
|
count = 1
|
|
} else {
|
|
this.keyedRecordCounts[groupingKey] += 1
|
|
count += 1
|
|
}
|
|
|
|
if count <= this.headCount {
|
|
outputChannel <- inrecAndContext
|
|
}
|
|
|
|
} else {
|
|
outputChannel <- inrecAndContext
|
|
}
|
|
}
|