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 RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
|
||||||
|
|
||||||
var Select = &OperationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: SelectOperator}
|
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 DeleteChild = &OperationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: DeleteChildOperator}
|
||||||
|
|
||||||
// var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35}
|
// 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(`:\s*`), opToken(CreateMap))
|
||||||
lexer.Add([]byte(`length`), opToken(Length))
|
lexer.Add([]byte(`length`), opToken(Length))
|
||||||
lexer.Add([]byte(`select`), opToken(Select))
|
lexer.Add([]byte(`select`), opToken(Select))
|
||||||
|
lexer.Add([]byte(`has`), opToken(Has))
|
||||||
lexer.Add([]byte(`explode`), opToken(Explode))
|
lexer.Add([]byte(`explode`), opToken(Explode))
|
||||||
lexer.Add([]byte(`or`), opToken(Or))
|
lexer.Add([]byte(`or`), opToken(Or))
|
||||||
lexer.Add([]byte(`and`), opToken(And))
|
lexer.Add([]byte(`and`), opToken(And))
|
||||||
|
Loading…
Reference in New Issue
Block a user