mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
min/max operators (#1992)
* min/max operators * min, max operator headers
This commit is contained in:
parent
3283c65dc4
commit
101cf14b8c
3
pkg/yqlib/doc/operators/headers/max.md
Normal file
3
pkg/yqlib/doc/operators/headers/max.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Max
|
||||
|
||||
Computes the maximum among an incoming sequence of scalar values.
|
3
pkg/yqlib/doc/operators/headers/min.md
Normal file
3
pkg/yqlib/doc/operators/headers/min.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Min
|
||||
|
||||
Computes the minimum among an incoming sequence of scalar values.
|
48
pkg/yqlib/doc/operators/max.md
Normal file
48
pkg/yqlib/doc/operators/max.md
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
## Maximum int
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- 99
|
||||
- 16
|
||||
- 12
|
||||
- 6
|
||||
- 66
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'max' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
99
|
||||
```
|
||||
|
||||
## Maximum string
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- foo
|
||||
- bar
|
||||
- baz
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'max' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
foo
|
||||
```
|
||||
|
||||
## Maximum of empty
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
[]
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'max' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
```
|
||||
|
48
pkg/yqlib/doc/operators/min.md
Normal file
48
pkg/yqlib/doc/operators/min.md
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
## Minimum int
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- 99
|
||||
- 16
|
||||
- 12
|
||||
- 6
|
||||
- 66
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'min' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
6
|
||||
```
|
||||
|
||||
## Minimum string
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- foo
|
||||
- bar
|
||||
- baz
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'min' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
bar
|
||||
```
|
||||
|
||||
## Minimum of empty
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
[]
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'min' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
```
|
||||
|
@ -199,6 +199,9 @@ var participleYqRules = []*participleYqRule{
|
||||
{"GreaterThan", `\s*>\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: true}), 0},
|
||||
{"LessThan", `\s*<\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: false}), 0},
|
||||
|
||||
simpleOp("min", minOpType),
|
||||
simpleOp("max", maxOpType),
|
||||
|
||||
{"AssignRelative", `\|=[c]*`, assignOpToken(true), 0},
|
||||
{"Assign", `=[c]*`, assignOpToken(false), 0},
|
||||
|
||||
|
@ -73,6 +73,8 @@ var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Ha
|
||||
var notEqualsOpType = &operationType{Type: "NOT_EQUALS", NumArgs: 2, Precedence: 40, Handler: notEqualsOperator}
|
||||
|
||||
var compareOpType = &operationType{Type: "COMPARE", NumArgs: 2, Precedence: 40, Handler: compareOperator}
|
||||
var minOpType = &operationType{Type: "MIN", NumArgs: 0, Precedence: 40, Handler: minOperator}
|
||||
var maxOpType = &operationType{Type: "MAX", NumArgs: 0, Precedence: 40, Handler: maxOperator}
|
||||
|
||||
// createmap needs to be above union, as we use union to build the components of the objects
|
||||
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 15, Handler: createMapOperator}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
@ -129,3 +130,40 @@ func compareScalars(context Context, prefs compareTypePref, lhs *CandidateNode,
|
||||
|
||||
return false, fmt.Errorf("%v not yet supported for comparison", lhs.Tag)
|
||||
}
|
||||
|
||||
func superlativeByComparison(d *dataTreeNavigator, context Context, prefs compareTypePref) (Context, error) {
|
||||
fn := compare(prefs)
|
||||
|
||||
var results = list.New()
|
||||
|
||||
for seq := context.MatchingNodes.Front(); seq != nil; seq = seq.Next() {
|
||||
splatted, err := splat(context.SingleChildContext(seq.Value.(*CandidateNode)), traversePreferences{})
|
||||
if err != nil {
|
||||
return Context{}, err
|
||||
}
|
||||
result := splatted.MatchingNodes.Front()
|
||||
if result != nil {
|
||||
for el := result.Next(); el != nil; el = el.Next() {
|
||||
cmp, err := fn(d, context, el.Value.(*CandidateNode), result.Value.(*CandidateNode))
|
||||
if err != nil {
|
||||
return Context{}, err
|
||||
}
|
||||
if isTruthyNode(cmp) {
|
||||
result = el
|
||||
}
|
||||
}
|
||||
results.PushBack(result.Value)
|
||||
}
|
||||
}
|
||||
return context.ChildContext(results), nil
|
||||
}
|
||||
|
||||
func minOperator(d *dataTreeNavigator, context Context, _ *ExpressionNode) (Context, error) {
|
||||
log.Debug(("Min"))
|
||||
return superlativeByComparison(d, context, compareTypePref{Greater: false})
|
||||
}
|
||||
|
||||
func maxOperator(d *dataTreeNavigator, context Context, _ *ExpressionNode) (Context, error) {
|
||||
log.Debug(("Max"))
|
||||
return superlativeByComparison(d, context, compareTypePref{Greater: true})
|
||||
}
|
||||
|
@ -383,3 +383,67 @@ func TestCompareOperatorScenarios(t *testing.T) {
|
||||
}
|
||||
documentOperatorScenarios(t, "compare", compareOperatorScenarios)
|
||||
}
|
||||
|
||||
var minOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Minimum int",
|
||||
document: "[99, 16, 12, 6, 66]\n",
|
||||
expression: `min`,
|
||||
expected: []string{
|
||||
"D0, P[3], (!!int)::6\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Minimum string",
|
||||
document: "[foo, bar, baz]\n",
|
||||
expression: `min`,
|
||||
expected: []string{
|
||||
"D0, P[1], (!!str)::bar\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Minimum of empty",
|
||||
document: "[]\n",
|
||||
expression: `min`,
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
func TestMinOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range minOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentOperatorScenarios(t, "min", minOperatorScenarios)
|
||||
}
|
||||
|
||||
var maxOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Maximum int",
|
||||
document: "[99, 16, 12, 6, 66]\n",
|
||||
expression: `max`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!int)::99\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Maximum string",
|
||||
document: "[foo, bar, baz]\n",
|
||||
expression: `max`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!str)::foo\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Maximum of empty",
|
||||
document: "[]\n",
|
||||
expression: `max`,
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
func TestMaxOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range maxOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentOperatorScenarios(t, "max", maxOperatorScenarios)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user