add support for negative filters (-F field!=regex)

This commit is contained in:
2025-01-30 17:30:02 +01:00
parent 6566dd66f0
commit 22d18344a8
8 changed files with 67 additions and 21 deletions

View File

@@ -22,7 +22,7 @@ Operational Flags:
-s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2024 Thomas von Dein
Copyright © 2022-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,6 +58,11 @@ type Pattern struct {
Negate bool
}
type Filter struct {
Regex *regexp.Regexp
Negate bool
}
// internal config
type Config struct {
Debug bool
@@ -100,7 +105,7 @@ type Config struct {
// used for field filtering
Rawfilters []string
Filters map[string]*regexp.Regexp
Filters map[string]Filter //map[string]*regexp.Regexp
// -r <file>
InputFile string
@@ -270,12 +275,20 @@ func (conf *Config) PrepareModeFlags(flag Modeflag) {
}
func (conf *Config) PrepareFilters() error {
conf.Filters = make(map[string]*regexp.Regexp, len(conf.Rawfilters))
conf.Filters = make(map[string]Filter, len(conf.Rawfilters))
for _, filter := range conf.Rawfilters {
parts := strings.Split(filter, "=")
for _, rawfilter := range conf.Rawfilters {
filter := Filter{}
parts := strings.Split(rawfilter, "!=")
if len(parts) != MAXPARTS {
return errors.New("filter field and value must be separated by =")
parts = strings.Split(rawfilter, "=")
if len(parts) != MAXPARTS {
return errors.New("filter field and value must be separated by '=' or '!='")
}
} else {
filter.Negate = true
}
reg, err := regexp.Compile(parts[1])
@@ -284,7 +297,8 @@ func (conf *Config) PrepareFilters() error {
parts[0], err)
}
conf.Filters[strings.ToLower(strings.ToLower(parts[0]))] = reg
filter.Regex = reg
conf.Filters[strings.ToLower(parts[0])] = filter
}
return nil

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2024 Thomas von Dein
Copyright © 2022-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -190,7 +190,7 @@ func Execute() {
// filters
rootCmd.PersistentFlags().StringArrayVarP(&conf.Rawfilters,
"filter", "F", nil, "Filter by field (field=regexp)")
"filter", "F", nil, "Filter by field (field=regexp || field!=regexp)")
rootCmd.PersistentFlags().StringArrayVarP(&conf.Transposers,
"regex-transposer", "R", nil, "apply /search/replace/ regexp to fields given in -T")

View File

@@ -17,7 +17,7 @@ SYNOPSIS
-s, --separator string Custom field separator
-k, --sort-by int|name Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
@@ -181,6 +181,10 @@ DESCRIPTION
If you specify more than one filter, both filters have to match (AND
operation).
These field filters can also be negated:
fieldname!=regexp
If the option -v is specified, the filtering is inverted.
COLUMNS
@@ -416,7 +420,7 @@ Operational Flags:
-s, --separator string Custom field separator
-k, --sort-by int|name Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2024 Thomas von Dein
Copyright © 2022-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -86,15 +86,19 @@ func FilterByFields(conf cfg.Config, data *Tabdata) (*Tabdata, bool, error) {
keep := true
for idx, header := range data.headers {
if !Exists(conf.Filters, strings.ToLower(header)) {
lcheader := strings.ToLower(header)
if !Exists(conf.Filters, lcheader) {
// do not filter by unspecified field
continue
}
if !conf.Filters[strings.ToLower(header)].MatchString(row[idx]) {
// there IS a filter, but it doesn't match
keep = false
match := conf.Filters[lcheader].Regex.MatchString(row[idx])
if conf.Filters[lcheader].Negate {
match = !match
}
if !match {
keep = false
break
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2024 Thomas von Dein
Copyright © 2024-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -98,6 +98,20 @@ func TestFilterByFields(t *testing.T) {
},
},
{
name: "one-field-negative",
filter: []string{"one!=asd"},
expect: Tabdata{
headers: []string{
"ONE", "TWO", "THREE",
},
entries: [][]string{
{"19191", "EDD 1", "x"},
{"8d8", "AN 1", "y"},
},
},
},
{
name: "one-field-inverted",
filter: []string{"one=19"},

View File

@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "TABLIZER 1"
.TH TABLIZER 1 "2025-01-22" "1" "User Commands"
.TH TABLIZER 1 "2025-01-30" "1" "User Commands"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -155,7 +155,7 @@ tablizer \- Manipulate tabular output of other programs
\& \-s, \-\-separator string Custom field separator
\& \-k, \-\-sort\-by int|name Sort by column (default: 1)
\& \-z, \-\-fuzzy Use fuzzy search [experimental]
\& \-F, \-\-filter field=reg Filter given field with regex, can be used multiple times
\& \-F, \-\-filter field[!]=reg Filter given field with regex, can be used multiple times
\& \-T, \-\-transpose\-columns string Transpose the speficied columns (separated by ,)
\& \-R, \-\-regex\-transposer /from/to/ Apply /search/replace/ regexp to fields given in \-T
\&
@@ -340,6 +340,12 @@ Fieldnames (== columns headers) are case insensitive.
If you specify more than one filter, both filters have to match (\s-1AND\s0
operation).
.PP
These field filters can also be negated:
.PP
.Vb 1
\& fieldname!=regexp
.Ve
.PP
If the option \fB\-v\fR is specified, the filtering is inverted.
.SS "\s-1COLUMNS\s0"
.IX Subsection "COLUMNS"

View File

@@ -16,7 +16,7 @@ tablizer - Manipulate tabular output of other programs
-s, --separator string Custom field separator
-k, --sort-by int|name Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
@@ -194,6 +194,10 @@ Fieldnames (== columns headers) are case insensitive.
If you specify more than one filter, both filters have to match (AND
operation).
These field filters can also be negated:
fieldname!=regexp
If the option B<-v> is specified, the filtering is inverted.