mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
Added has operator
This commit is contained in:
parent
3f04a1b52e
commit
3d6a231722
44
pkg/yqlib/doc/Has.md
Normal file
44
pkg/yqlib/doc/Has.md
Normal file
@ -0,0 +1,44 @@
|
||||
This is operation that returns true if the key exists in a map (or index in an array), false otherwise.
|
||||
## Has map key
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- a: yes
|
||||
- a: ~
|
||||
- a:
|
||||
- b: nope
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[] | has("a")' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
true
|
||||
true
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
## Has array index
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- []
|
||||
- [1]
|
||||
- [1, 2]
|
||||
- [1, null]
|
||||
- [1, 2, 3]
|
||||
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[] | has(1)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
true
|
||||
```
|
||||
|
1
pkg/yqlib/doc/headers/Has.md
Normal file
1
pkg/yqlib/doc/headers/Has.md
Normal file
@ -0,0 +1 @@
|
||||
This is operation that returns true if the key exists in a map (or index in an array), false otherwise.
|
@ -66,6 +66,7 @@ var Empty = &OperationType{Type: "EMPTY", NumArgs: 50, Handler: EmptyOperator}
|
||||
var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
|
||||
|
||||
var Select = &OperationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: SelectOperator}
|
||||
var Has = &OperationType{Type: "HAS", NumArgs: 1, Precedence: 50, Handler: HasOperator}
|
||||
var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: DeleteChildOperator}
|
||||
|
||||
// var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35}
|
||||
|
53
pkg/yqlib/operator_has.go
Normal file
53
pkg/yqlib/operator_has.go
Normal file
@ -0,0 +1,53 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"strconv"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func HasOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
|
||||
log.Debugf("-- hasOperation")
|
||||
var results = list.New()
|
||||
|
||||
rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
|
||||
wanted := rhs.Front().Value.(*CandidateNode).Node
|
||||
wantedKey := wanted.Value
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
|
||||
// grab the first value
|
||||
var contents = candidate.Node.Content
|
||||
switch candidate.Node.Kind {
|
||||
case yaml.MappingNode:
|
||||
candidateHasKey := false
|
||||
for index := 0; index < len(contents) && !candidateHasKey; index = index + 2 {
|
||||
key := contents[index]
|
||||
if key.Value == wantedKey {
|
||||
candidateHasKey = true
|
||||
}
|
||||
}
|
||||
results.PushBack(createBooleanCandidate(candidate, candidateHasKey))
|
||||
case yaml.SequenceNode:
|
||||
candidateHasKey := false
|
||||
if wanted.Tag == "!!int" {
|
||||
var number, errParsingInt = strconv.ParseInt(wantedKey, 10, 64) // nolint
|
||||
if errParsingInt != nil {
|
||||
return nil, errParsingInt
|
||||
}
|
||||
candidateHasKey = int64(len(contents)) > number
|
||||
}
|
||||
results.PushBack(createBooleanCandidate(candidate, candidateHasKey))
|
||||
default:
|
||||
results.PushBack(createBooleanCandidate(candidate, false))
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
48
pkg/yqlib/operator_has_test.go
Normal file
48
pkg/yqlib/operator_has_test.go
Normal file
@ -0,0 +1,48 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var hasOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Has map key",
|
||||
document: `- a: "yes"
|
||||
- a: ~
|
||||
- a:
|
||||
- b: nope
|
||||
`,
|
||||
expression: `.[] | has("a")`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!bool)::true\n",
|
||||
"D0, P[1], (!!bool)::true\n",
|
||||
"D0, P[2], (!!bool)::true\n",
|
||||
"D0, P[3], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
dontFormatInputForDoc: true,
|
||||
description: "Has array index",
|
||||
document: `- []
|
||||
- [1]
|
||||
- [1, 2]
|
||||
- [1, null]
|
||||
- [1, 2, 3]
|
||||
`,
|
||||
expression: `.[] | has(1)`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!bool)::false\n",
|
||||
"D0, P[1], (!!bool)::false\n",
|
||||
"D0, P[2], (!!bool)::true\n",
|
||||
"D0, P[3], (!!bool)::true\n",
|
||||
"D0, P[4], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestHasOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range hasOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Has", hasOperatorScenarios)
|
||||
}
|
@ -191,6 +191,7 @@ func initLexer() (*lex.Lexer, error) {
|
||||
lexer.Add([]byte(`:\s*`), opToken(CreateMap))
|
||||
lexer.Add([]byte(`length`), opToken(Length))
|
||||
lexer.Add([]byte(`select`), opToken(Select))
|
||||
lexer.Add([]byte(`has`), opToken(Has))
|
||||
lexer.Add([]byte(`explode`), opToken(Explode))
|
||||
lexer.Add([]byte(`or`), opToken(Or))
|
||||
lexer.Add([]byte(`and`), opToken(And))
|
||||
|
Loading…
Reference in New Issue
Block a user