mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-12 05:38: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},
|
{"GreaterThan", `\s*>\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: true}), 0},
|
||||||
{"LessThan", `\s*<\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: false}), 0},
|
{"LessThan", `\s*<\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: false}), 0},
|
||||||
|
|
||||||
|
simpleOp("min", minOpType),
|
||||||
|
simpleOp("max", maxOpType),
|
||||||
|
|
||||||
{"AssignRelative", `\|=[c]*`, assignOpToken(true), 0},
|
{"AssignRelative", `\|=[c]*`, assignOpToken(true), 0},
|
||||||
{"Assign", `=[c]*`, assignOpToken(false), 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 notEqualsOpType = &operationType{Type: "NOT_EQUALS", NumArgs: 2, Precedence: 40, Handler: notEqualsOperator}
|
||||||
|
|
||||||
var compareOpType = &operationType{Type: "COMPARE", NumArgs: 2, Precedence: 40, Handler: compareOperator}
|
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
|
// 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}
|
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 15, Handler: createMapOperator}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"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)
|
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)
|
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