Implement all/by-regex field selection (-a/-r) for mlr sub, gsub, and ssub (#1480)

* Code-dedupe `sub`, `gsub`, and `ssub` verbs

* More dedupe

* Start with -a

* Implement -r

* unit-test cases

* Windows command-line parsing
This commit is contained in:
John Kerl 2024-01-23 17:18:13 -05:00 committed by GitHub
parent 81d11365a0
commit e5ec9f67bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 475 additions and 518 deletions

View file

@ -1,157 +0,0 @@
package transformers
import (
"container/list"
"fmt"
"os"
"strings"
"github.com/johnkerl/miller/pkg/bifs"
"github.com/johnkerl/miller/pkg/cli"
"github.com/johnkerl/miller/pkg/mlrval"
"github.com/johnkerl/miller/pkg/types"
)
// ----------------------------------------------------------------
const verbNameGsub = "gsub"
var GsubSetup = TransformerSetup{
Verb: verbNameGsub,
UsageFunc: transformerGsubUsage,
ParseCLIFunc: transformerGsubParseCLI,
IgnoresInput: false,
}
func transformerGsubUsage(
o *os.File,
) {
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameGsub)
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
fmt.Fprintf(o, "for the old string and handling multiple matches, like the `gsub` DSL function.\n")
fmt.Fprintf(o, "See also the `sub` and `ssub` verbs.\n")
fmt.Fprintf(o, "Options:\n")
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
fmt.Fprintf(o, "-h|--help Show this message.\n")
}
func transformerGsubParseCLI(
pargi *int,
argc int,
args []string,
_ *cli.TOptions,
doConstruct bool, // false for first pass of CLI-parse, true for second pass
) IRecordTransformer {
// Skip the verb name from the current spot in the mlr command line
argi := *pargi
verb := args[argi]
argi++
// Parse local flags
var fieldNames []string = nil
var oldText string
var newText string
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
}
if args[argi] == "--" {
break // All transformers must do this so main-flags can follow verb-flags
}
argi++
if opt == "-h" || opt == "--help" {
transformerGsubUsage(os.Stdout)
os.Exit(0)
} else if opt == "-f" {
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
} else {
transformerGsubUsage(os.Stderr)
os.Exit(1)
}
}
if fieldNames == nil {
transformerGsubUsage(os.Stderr)
os.Exit(1)
}
// Get the old and new text from the command line
if (argc - argi) < 2 {
transformerGsubUsage(os.Stderr)
os.Exit(1)
}
oldText = args[argi]
newText = args[argi+1]
argi += 2
*pargi = argi
if !doConstruct { // All transformers must do this for main command-line parsing
return nil
}
transformer, err := NewTransformerGsub(
fieldNames,
oldText,
newText,
)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return transformer
}
// ----------------------------------------------------------------
type TransformerGsub struct {
fieldNames []string
oldText *mlrval.Mlrval
newText *mlrval.Mlrval
}
// ----------------------------------------------------------------
func NewTransformerGsub(
fieldNames []string,
oldText string,
newText string,
) (*TransformerGsub, error) {
tr := &TransformerGsub{
fieldNames: fieldNames,
oldText: mlrval.FromString(oldText),
newText: mlrval.FromString(newText),
}
return tr, nil
}
func (tr *TransformerGsub) Transform(
inrecAndContext *types.RecordAndContext,
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
inputDownstreamDoneChannel <-chan bool,
outputDownstreamDoneChannel chan<- bool,
) {
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
if !inrecAndContext.EndOfStream {
inrec := inrecAndContext.Record
for _, fieldName := range tr.fieldNames {
oldValue := inrec.Get(fieldName)
if oldValue == nil {
continue
}
newValue := bifs.BIF_gsub(oldValue, tr.oldText, tr.newText)
inrec.PutReference(fieldName, newValue)
}
outputRecordsAndContexts.PushBack(inrecAndContext)
} else {
outputRecordsAndContexts.PushBack(inrecAndContext) // emit end-of-stream marker
}
}

View file

@ -1,156 +0,0 @@
package transformers
import (
"container/list"
"fmt"
"os"
"strings"
"github.com/johnkerl/miller/pkg/bifs"
"github.com/johnkerl/miller/pkg/cli"
"github.com/johnkerl/miller/pkg/mlrval"
"github.com/johnkerl/miller/pkg/types"
)
// ----------------------------------------------------------------
const verbNameSsub = "ssub"
var SsubSetup = TransformerSetup{
Verb: verbNameSsub,
UsageFunc: transformerSsubUsage,
ParseCLIFunc: transformerSsubParseCLI,
IgnoresInput: false,
}
func transformerSsubUsage(
o *os.File,
) {
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSsub)
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), without regex support for\n")
fmt.Fprintf(o, "the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.\n")
fmt.Fprintf(o, "Options:\n")
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
fmt.Fprintf(o, "-h|--help Show this message.\n")
}
func transformerSsubParseCLI(
pargi *int,
argc int,
args []string,
_ *cli.TOptions,
doConstruct bool, // false for first pass of CLI-parse, true for second pass
) IRecordTransformer {
// Skip the verb name from the current spot in the mlr command line
argi := *pargi
verb := args[argi]
argi++
// Parse local flags
var fieldNames []string = nil
var oldText string
var newText string
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
}
if args[argi] == "--" {
break // All transformers must do this so main-flags can follow verb-flags
}
argi++
if opt == "-h" || opt == "--help" {
transformerSsubUsage(os.Stdout)
os.Exit(0)
} else if opt == "-f" {
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
} else {
transformerSsubUsage(os.Stderr)
os.Exit(1)
}
}
if fieldNames == nil {
transformerSsubUsage(os.Stderr)
os.Exit(1)
}
// Get the old and new text from the command line
if (argc - argi) < 2 {
transformerSsubUsage(os.Stderr)
os.Exit(1)
}
oldText = args[argi]
newText = args[argi+1]
argi += 2
*pargi = argi
if !doConstruct { // All transformers must do this for main command-line parsing
return nil
}
transformer, err := NewTransformerSsub(
fieldNames,
oldText,
newText,
)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return transformer
}
// ----------------------------------------------------------------
type TransformerSsub struct {
fieldNames []string
oldText *mlrval.Mlrval
newText *mlrval.Mlrval
}
// ----------------------------------------------------------------
func NewTransformerSsub(
fieldNames []string,
oldText string,
newText string,
) (*TransformerSsub, error) {
tr := &TransformerSsub{
fieldNames: fieldNames,
oldText: mlrval.FromString(oldText),
newText: mlrval.FromString(newText),
}
return tr, nil
}
func (tr *TransformerSsub) Transform(
inrecAndContext *types.RecordAndContext,
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
inputDownstreamDoneChannel <-chan bool,
outputDownstreamDoneChannel chan<- bool,
) {
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
if !inrecAndContext.EndOfStream {
inrec := inrecAndContext.Record
for _, fieldName := range tr.fieldNames {
oldValue := inrec.Get(fieldName)
if oldValue == nil {
continue
}
newValue := bifs.BIF_ssub(oldValue, tr.oldText, tr.newText)
inrec.PutReference(fieldName, newValue)
}
outputRecordsAndContexts.PushBack(inrecAndContext)
} else {
outputRecordsAndContexts.PushBack(inrecAndContext) // emit end-of-stream marker
}
}

View file

@ -1,157 +0,0 @@
package transformers
import (
"container/list"
"fmt"
"os"
"strings"
"github.com/johnkerl/miller/pkg/bifs"
"github.com/johnkerl/miller/pkg/cli"
"github.com/johnkerl/miller/pkg/mlrval"
"github.com/johnkerl/miller/pkg/types"
)
// ----------------------------------------------------------------
const verbNameSub = "sub"
var SubSetup = TransformerSetup{
Verb: verbNameSub,
UsageFunc: transformerSubUsage,
ParseCLIFunc: transformerSubParseCLI,
IgnoresInput: false,
}
func transformerSubUsage(
o *os.File,
) {
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSub)
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
fmt.Fprintf(o, "for the old string and not handling multiple matches, like the `sub` DSL function.\n")
fmt.Fprintf(o, "See also the `gsub` and `ssub` verbs.\n")
fmt.Fprintf(o, "Options:\n")
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
fmt.Fprintf(o, "-h|--help Show this message.\n")
}
func transformerSubParseCLI(
pargi *int,
argc int,
args []string,
_ *cli.TOptions,
doConstruct bool, // false for first pass of CLI-parse, true for second pass
) IRecordTransformer {
// Skip the verb name from the current spot in the mlr command line
argi := *pargi
verb := args[argi]
argi++
// Parse local flags
var fieldNames []string = nil
var oldText string
var newText string
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
}
if args[argi] == "--" {
break // All transformers must do this so main-flags can follow verb-flags
}
argi++
if opt == "-h" || opt == "--help" {
transformerSubUsage(os.Stdout)
os.Exit(0)
} else if opt == "-f" {
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
} else {
transformerSubUsage(os.Stderr)
os.Exit(1)
}
}
if fieldNames == nil {
transformerSubUsage(os.Stderr)
os.Exit(1)
}
// Get the old and new text from the command line
if (argc - argi) < 2 {
transformerSubUsage(os.Stderr)
os.Exit(1)
}
oldText = args[argi]
newText = args[argi+1]
argi += 2
*pargi = argi
if !doConstruct { // All transformers must do this for main command-line parsing
return nil
}
transformer, err := NewTransformerSub(
fieldNames,
oldText,
newText,
)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return transformer
}
// ----------------------------------------------------------------
type TransformerSub struct {
fieldNames []string
oldText *mlrval.Mlrval
newText *mlrval.Mlrval
}
// ----------------------------------------------------------------
func NewTransformerSub(
fieldNames []string,
oldText string,
newText string,
) (*TransformerSub, error) {
tr := &TransformerSub{
fieldNames: fieldNames,
oldText: mlrval.FromString(oldText),
newText: mlrval.FromString(newText),
}
return tr, nil
}
func (tr *TransformerSub) Transform(
inrecAndContext *types.RecordAndContext,
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
inputDownstreamDoneChannel <-chan bool,
outputDownstreamDoneChannel chan<- bool,
) {
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
if !inrecAndContext.EndOfStream {
inrec := inrecAndContext.Record
for _, fieldName := range tr.fieldNames {
oldValue := inrec.Get(fieldName)
if oldValue == nil {
continue
}
newValue := bifs.BIF_sub(oldValue, tr.oldText, tr.newText)
inrec.PutReference(fieldName, newValue)
}
outputRecordsAndContexts.PushBack(inrecAndContext)
} else {
outputRecordsAndContexts.PushBack(inrecAndContext) // emit end-of-stream marker
}
}

355
pkg/transformers/subs.go Normal file
View file

@ -0,0 +1,355 @@
package transformers
import (
"container/list"
"fmt"
"os"
"regexp"
"strings"
"github.com/johnkerl/miller/pkg/bifs"
"github.com/johnkerl/miller/pkg/cli"
"github.com/johnkerl/miller/pkg/lib"
"github.com/johnkerl/miller/pkg/mlrval"
"github.com/johnkerl/miller/pkg/types"
)
// ----------------------------------------------------------------
const verbNameSub = "sub"
const verbNameGsub = "gsub"
const verbNameSsub = "ssub"
var SubSetup = TransformerSetup{
Verb: verbNameSub,
UsageFunc: transformerSubUsage,
ParseCLIFunc: transformerSubParseCLI,
IgnoresInput: false,
}
var GsubSetup = TransformerSetup{
Verb: verbNameGsub,
UsageFunc: transformerGsubUsage,
ParseCLIFunc: transformerGsubParseCLI,
IgnoresInput: false,
}
var SsubSetup = TransformerSetup{
Verb: verbNameSsub,
UsageFunc: transformerSsubUsage,
ParseCLIFunc: transformerSsubParseCLI,
IgnoresInput: false,
}
func transformerSubUsage(
o *os.File,
) {
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSub)
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
fmt.Fprintf(o, "for the old string and not handling multiple matches, like the `sub` DSL function.\n")
fmt.Fprintf(o, "See also the `gsub` and `ssub` verbs.\n")
fmt.Fprintf(o, "Options:\n")
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
fmt.Fprintf(o, "-h|--help Show this message.\n")
}
func transformerGsubUsage(
o *os.File,
) {
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameGsub)
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
fmt.Fprintf(o, "for the old string and handling multiple matches, like the `gsub` DSL function.\n")
fmt.Fprintf(o, "See also the `sub` and `ssub` verbs.\n")
fmt.Fprintf(o, "Options:\n")
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
fmt.Fprintf(o, "-h|--help Show this message.\n")
}
func transformerSsubUsage(
o *os.File,
) {
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSsub)
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), without regex support for\n")
fmt.Fprintf(o, "the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.\n")
fmt.Fprintf(o, "Options:\n")
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
fmt.Fprintf(o, "-h|--help Show this message.\n")
}
type subConstructorFunc func(
fieldNames []string,
doAllFieldNames bool,
doRegexes bool,
oldText string,
newText string,
) (IRecordTransformer, error)
type fieldAcceptorFunc func(
fieldName string,
) bool
func transformerSubParseCLI(
pargi *int,
argc int,
args []string,
opts *cli.TOptions,
doConstruct bool, // false for first pass of CLI-parse, true for second pass
) IRecordTransformer {
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSubUsage, NewTransformerSub)
}
func transformerGsubParseCLI(
pargi *int,
argc int,
args []string,
opts *cli.TOptions,
doConstruct bool, // false for first pass of CLI-parse, true for second pass
) IRecordTransformer {
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerGsubUsage, NewTransformerGsub)
}
func transformerSsubParseCLI(
pargi *int,
argc int,
args []string,
opts *cli.TOptions,
doConstruct bool, // false for first pass of CLI-parse, true for second pass
) IRecordTransformer {
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSsubUsage, NewTransformerSsub)
}
// transformerSubsParseCLI is a shared CLI-parser for the sub, gsub, and ssub verbs.
func transformerSubsParseCLI(
pargi *int,
argc int,
args []string,
_ *cli.TOptions,
doConstruct bool, // false for first pass of CLI-parse, true for second pass
usageFunc TransformerUsageFunc,
constructorFunc subConstructorFunc,
) IRecordTransformer {
// Skip the verb name from the current spot in the mlr command line
argi := *pargi
verb := args[argi]
argi++
// Parse local flags
var fieldNames []string = nil
doAllFieldNames := false
doRegexes := false
var oldText string
var newText string
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
}
if args[argi] == "--" {
break // All transformers must do this so main-flags can follow verb-flags
}
argi++
if opt == "-h" || opt == "--help" {
usageFunc(os.Stdout)
os.Exit(0)
} else if opt == "-a" {
doAllFieldNames = true
doRegexes = false
fieldNames = nil
} else if opt == "-r" {
doRegexes = true
} else if opt == "-f" {
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
doAllFieldNames = false
} else {
usageFunc(os.Stderr)
os.Exit(1)
}
}
if fieldNames == nil && !doAllFieldNames {
usageFunc(os.Stderr)
os.Exit(1)
}
// Get the old and new text from the command line
if (argc - argi) < 2 {
usageFunc(os.Stderr)
os.Exit(1)
}
oldText = args[argi]
newText = args[argi+1]
argi += 2
*pargi = argi
if !doConstruct { // All transformers must do this for main command-line parsing
return nil
}
transformer, err := constructorFunc(
fieldNames,
doAllFieldNames,
doRegexes,
oldText,
newText,
)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return transformer
}
type TransformerSubs struct {
fieldNamesSet map[string]bool // for -f
regexes []*regexp.Regexp // for -r
oldText *mlrval.Mlrval
newText *mlrval.Mlrval
fieldAcceptor fieldAcceptorFunc // for -f, -r, -a
subber bifs.TernaryFunc // for sub, gsub, ssub
}
func NewTransformerSub(
fieldNames []string,
doAllFieldNames bool,
doRegexes bool,
oldText string,
newText string,
) (IRecordTransformer, error) {
return NewTransformerSubs(fieldNames, doAllFieldNames, doRegexes, oldText, newText, safe_sub)
}
func NewTransformerGsub(
fieldNames []string,
doAllFieldNames bool,
doRegexes bool,
oldText string,
newText string,
) (IRecordTransformer, error) {
return NewTransformerSubs(fieldNames, doAllFieldNames, doRegexes, oldText, newText, safe_gsub)
}
func NewTransformerSsub(
fieldNames []string,
doAllFieldNames bool,
doRegexes bool,
oldText string,
newText string,
) (IRecordTransformer, error) {
return NewTransformerSubs(fieldNames, doAllFieldNames, doRegexes, oldText, newText, safe_ssub)
}
func NewTransformerSubs(
fieldNames []string,
doAllFieldNames bool,
doRegexes bool,
oldText string,
newText string,
subber bifs.TernaryFunc,
) (IRecordTransformer, error) {
tr := &TransformerSubs{
fieldNamesSet: lib.StringListToSet(fieldNames),
oldText: mlrval.FromString(oldText),
newText: mlrval.FromString(newText),
subber: subber,
}
if doAllFieldNames {
tr.fieldAcceptor = tr.fieldAcceptorAll
} else if doRegexes {
tr.fieldAcceptor = tr.fieldAcceptorByRegexes
tr.regexes = make([]*regexp.Regexp, len(fieldNames))
for i, regexString := range fieldNames {
// Handles "a.*b"i Miller case-insensitive-regex specification
regex, err := lib.CompileMillerRegex(regexString)
if err != nil {
fmt.Fprintf(os.Stderr, "%s %s: cannot compile regex [%s]\n", "mlr", verbNameCut, regexString)
os.Exit(1)
}
tr.regexes[i] = regex
}
} else {
tr.fieldAcceptor = tr.fieldAcceptorByNames
}
return tr, nil
}
func (tr *TransformerSubs) Transform(
inrecAndContext *types.RecordAndContext,
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
inputDownstreamDoneChannel <-chan bool,
outputDownstreamDoneChannel chan<- bool,
) {
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
if !inrecAndContext.EndOfStream {
inrec := inrecAndContext.Record
// Run sub, gsub, or ssub on the user-specified field names
for pe := inrec.Head; pe != nil; pe = pe.Next {
if tr.fieldAcceptor(pe.Key) {
pe.Value = tr.subber(pe.Value, tr.oldText, tr.newText)
}
}
}
// Including emit of end-of-stream marker
outputRecordsAndContexts.PushBack(inrecAndContext)
}
// fieldAcceptorByNames implements -f
func (tr *TransformerSubs) fieldAcceptorByNames(
fieldName string,
) bool {
return tr.fieldNamesSet[fieldName]
}
// fieldAcceptorByNames implements -r
func (tr *TransformerSubs) fieldAcceptorByRegexes(
fieldName string,
) bool {
for _, regex := range tr.regexes {
if regex.MatchString(fieldName) {
return true
}
}
return false
}
// fieldAcceptorByNames implements -a
func (tr *TransformerSubs) fieldAcceptorAll(
fieldName string,
) bool {
return true
}
// safe_sub implements sub, but doesn't produce error-type on non-string input.
func safe_sub(input1, input2, input3 *mlrval.Mlrval) *mlrval.Mlrval {
if input1.IsString() {
return bifs.BIF_sub(input1, input2, input3)
} else {
return input1
}
}
// safe_gsub implements gsub, but doesn't produce error-type on non-string input.
func safe_gsub(input1, input2, input3 *mlrval.Mlrval) *mlrval.Mlrval {
if input1.IsString() {
return bifs.BIF_gsub(input1, input2, input3)
} else {
return input1
}
}
// safe_ssub implements ssub, but doesn't produce error-type on non-string input.
func safe_ssub(input1, input2, input3 *mlrval.Mlrval) *mlrval.Mlrval {
if input1.IsString() {
return bifs.BIF_ssub(input1, input2, input3)
} else {
return input1
}
}

View file

@ -1 +0,0 @@
mlr --d2p --from test/input/abixy sub -f a,b e X

View file

@ -1,11 +0,0 @@
a b i x y
pan pan 1 0.34679014 0.72680286
Xks pan 2 0.75867996 0.52215111
wyX wyX 3 0.20460331 0.33831853
Xks wyX 4 0.38139939 0.13418874
wyX pan 5 0.57328892 0.86362447
zXe pan 6 0.52712616 0.49322129
Xks zXe 7 0.61178406 0.18788492
zXe wyX 8 0.59855401 0.97618139
hat wyX 9 0.03144188 0.74955076
pan wyX 10 0.50262601 0.95261836

View file

@ -1 +0,0 @@
mlr --d2p --from test/input/abixy gsub -f a,b e X

View file

@ -1,11 +0,0 @@
a b i x y
pan pan 1 0.34679014 0.72680286
Xks pan 2 0.75867996 0.52215111
wyX wyX 3 0.20460331 0.33831853
Xks wyX 4 0.38139939 0.13418874
wyX pan 5 0.57328892 0.86362447
zXX pan 6 0.52712616 0.49322129
Xks zXX 7 0.61178406 0.18788492
zXX wyX 8 0.59855401 0.97618139
hat wyX 9 0.03144188 0.74955076
pan wyX 10 0.50262601 0.95261836

View file

@ -1 +0,0 @@
mlr --d2p --from test/input/abixy sub -f a,b . X

View file

@ -1,11 +0,0 @@
a b i x y
Xan Xan 1 0.34679014 0.72680286
Xks Xan 2 0.75867996 0.52215111
Xye Xye 3 0.20460331 0.33831853
Xks Xye 4 0.38139939 0.13418874
Xye Xan 5 0.57328892 0.86362447
Xee Xan 6 0.52712616 0.49322129
Xks Xee 7 0.61178406 0.18788492
Xee Xye 8 0.59855401 0.97618139
Xat Xye 9 0.03144188 0.74955076
Xan Xye 10 0.50262601 0.95261836

View file

@ -1 +0,0 @@
mlr --d2p --from test/input/abixy ssub -f a,b e X

View file

@ -1,11 +0,0 @@
a b i x y
pan pan 1 0.34679014 0.72680286
Xks pan 2 0.75867996 0.52215111
wyX wyX 3 0.20460331 0.33831853
Xks wyX 4 0.38139939 0.13418874
wyX pan 5 0.57328892 0.86362447
zXe pan 6 0.52712616 0.49322129
Xks zXe 7 0.61178406 0.18788492
zXe wyX 8 0.59855401 0.97618139
hat wyX 9 0.03144188 0.74955076
pan wyX 10 0.50262601 0.95261836

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv gsub -a l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yeXXow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square faXse 4 48 77.55420000 7.46700000
purpXe triangXe faXse 5 51 81.22900000 8.59100000
red square faXse 6 64 77.19910000 9.53100000
purpXe triangXe faXse 7 65 80.14050000 5.82400000
yeXXow circXe true 8 73 63.97850000 4.23700000
yeXXow circXe true 9 87 63.50580000 8.33500000
purpXe square faXse 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv gsub -f color,shape,index l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yeXXow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square false 4 48 77.55420000 7.46700000
purpXe triangXe false 5 51 81.22900000 8.59100000
red square false 6 64 77.19910000 9.53100000
purpXe triangXe false 7 65 80.14050000 5.82400000
yeXXow circXe true 8 73 63.97850000 4.23700000
yeXXow circXe true 9 87 63.50580000 8.33500000
purpXe square false 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv gsub -r -f '.*e' l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yellow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square false 4 48 77.55420000 7.46700000
purple triangXe false 5 51 81.22900000 8.59100000
red square false 6 64 77.19910000 9.53100000
purple triangXe false 7 65 80.14050000 5.82400000
yellow circXe true 8 73 63.97850000 4.23700000
yellow circXe true 9 87 63.50580000 8.33500000
purple square false 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv ssub -r -f '.*e' l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yellow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square false 4 48 77.55420000 7.46700000
purple triangXe false 5 51 81.22900000 8.59100000
red square false 6 64 77.19910000 9.53100000
purple triangXe false 7 65 80.14050000 5.82400000
yellow circXe true 8 73 63.97850000 4.23700000
yellow circXe true 9 87 63.50580000 8.33500000
purple square false 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv sub -r -f '.*e' l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yellow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square false 4 48 77.55420000 7.46700000
purple triangXe false 5 51 81.22900000 8.59100000
red square false 6 64 77.19910000 9.53100000
purple triangXe false 7 65 80.14050000 5.82400000
yellow circXe true 8 73 63.97850000 4.23700000
yellow circXe true 9 87 63.50580000 8.33500000
purple square false 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv ssub -a l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yeXlow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square faXse 4 48 77.55420000 7.46700000
purpXe triangXe faXse 5 51 81.22900000 8.59100000
red square faXse 6 64 77.19910000 9.53100000
purpXe triangXe faXse 7 65 80.14050000 5.82400000
yeXlow circXe true 8 73 63.97850000 4.23700000
yeXlow circXe true 9 87 63.50580000 8.33500000
purpXe square faXse 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv ssub -f color,shape,index l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yeXlow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square false 4 48 77.55420000 7.46700000
purpXe triangXe false 5 51 81.22900000 8.59100000
red square false 6 64 77.19910000 9.53100000
purpXe triangXe false 7 65 80.14050000 5.82400000
yeXlow circXe true 8 73 63.97850000 4.23700000
yeXlow circXe true 9 87 63.50580000 8.33500000
purpXe square false 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv sub -a l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yeXlow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square faXse 4 48 77.55420000 7.46700000
purpXe triangXe faXse 5 51 81.22900000 8.59100000
red square faXse 6 64 77.19910000 9.53100000
purpXe triangXe faXse 7 65 80.14050000 5.82400000
yeXlow circXe true 8 73 63.97850000 4.23700000
yeXlow circXe true 9 87 63.50580000 8.33500000
purpXe square faXse 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv sub -f a,b l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yellow triangle true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circle true 3 16 13.81030000 2.90100000
red square false 4 48 77.55420000 7.46700000
purple triangle false 5 51 81.22900000 8.59100000
red square false 6 64 77.19910000 9.53100000
purple triangle false 7 65 80.14050000 5.82400000
yellow circle true 8 73 63.97850000 4.23700000
yellow circle true 9 87 63.50580000 8.33500000
purple square false 10 91 72.37350000 8.24300000

View file

@ -0,0 +1 @@
mlr --c2p --from test/input/example.csv sub -f color,shape,index l X

View file

@ -0,0 +1,11 @@
color shape flag k index quantity rate
yeXlow triangXe true 1 11 43.64980000 9.88700000
red square true 2 15 79.27780000 0.01300000
red circXe true 3 16 13.81030000 2.90100000
red square false 4 48 77.55420000 7.46700000
purpXe triangXe false 5 51 81.22900000 8.59100000
red square false 6 64 77.19910000 9.53100000
purpXe triangXe false 7 65 80.14050000 5.82400000
yeXlow circXe true 8 73 63.97850000 4.23700000
yeXlow circXe true 9 87 63.50580000 8.33500000
purpXe square false 10 91 72.37350000 8.24300000