mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
Added map, map_values
This commit is contained in:
parent
54b355bffb
commit
14f8f92b76
3
pkg/yqlib/doc/headers/map.md
Normal file
3
pkg/yqlib/doc/headers/map.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Map
|
||||||
|
|
||||||
|
Maps values of an array. Use `map_values` to map values of an object.
|
40
pkg/yqlib/doc/map.md
Normal file
40
pkg/yqlib/doc/map.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Map
|
||||||
|
|
||||||
|
Maps values of an array. Use `map_values` to map values of an object.
|
||||||
|
|
||||||
|
## Map array
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval 'map(. + 1)' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Map object values
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: 1
|
||||||
|
b: 2
|
||||||
|
c: 3
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval 'map_values(. + 1)' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: 2
|
||||||
|
b: 3
|
||||||
|
c: 4
|
||||||
|
```
|
||||||
|
|
@ -312,6 +312,9 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`:\s*`), opToken(createMapOpType))
|
lexer.Add([]byte(`:\s*`), opToken(createMapOpType))
|
||||||
lexer.Add([]byte(`length`), opToken(lengthOpType))
|
lexer.Add([]byte(`length`), opToken(lengthOpType))
|
||||||
|
|
||||||
|
lexer.Add([]byte(`map`), opToken(mapOpType))
|
||||||
|
lexer.Add([]byte(`map_values`), opToken(mapValuesOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`flatten\([0-9]+\)`), flattenWithDepth())
|
lexer.Add([]byte(`flatten\([0-9]+\)`), flattenWithDepth())
|
||||||
lexer.Add([]byte(`flatten`), opTokenWithPrefs(flattenOpType, nil, flattenPreferences{depth: -1}))
|
lexer.Add([]byte(`flatten`), opTokenWithPrefs(flattenOpType, nil, flattenPreferences{depth: -1}))
|
||||||
|
|
||||||
|
@ -64,6 +64,8 @@ var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence:
|
|||||||
|
|
||||||
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
||||||
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
|
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
|
||||||
|
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}
|
||||||
|
var mapValuesOpType = &operationType{Type: "MAP_VALUES", NumArgs: 1, Precedence: 50, Handler: mapValuesOperator}
|
||||||
var encodeOpType = &operationType{Type: "ENCODE", NumArgs: 0, Precedence: 50, Handler: encodeOperator}
|
var encodeOpType = &operationType{Type: "ENCODE", NumArgs: 0, Precedence: 50, Handler: encodeOperator}
|
||||||
var decodeOpType = &operationType{Type: "DECODE", NumArgs: 0, Precedence: 50, Handler: decodeOperator}
|
var decodeOpType = &operationType{Type: "DECODE", NumArgs: 0, Precedence: 50, Handler: decodeOperator}
|
||||||
|
|
||||||
|
64
pkg/yqlib/operator_map.go
Normal file
64
pkg/yqlib/operator_map.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mapValuesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
//run expression against entries
|
||||||
|
// splat toEntries and pipe it into Rhs
|
||||||
|
splatted, err := splat(d, context.SingleChildContext(candidate), traversePreferences{})
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
assignUpdateExp := &ExpressionNode{
|
||||||
|
Operation: &Operation{OperationType: assignOpType, UpdateAssign: true},
|
||||||
|
Rhs: expressionNode.Rhs,
|
||||||
|
}
|
||||||
|
_, err = assignUpdateOperator(d, splatted, assignUpdateExp)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
|
var results = list.New()
|
||||||
|
|
||||||
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
//run expression against entries
|
||||||
|
// splat toEntries and pipe it into Rhs
|
||||||
|
splatted, err := splat(d, context.SingleChildContext(candidate), traversePreferences{})
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := d.GetMatchingNodes(splatted, expressionNode.Rhs)
|
||||||
|
log.Debug("expressionNode.Rhs %v", expressionNode.Rhs.Operation.OperationType)
|
||||||
|
log.Debug("result %v", result)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
selfExpression := &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
||||||
|
collected, err := collectTogether(d, result, selfExpression)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
collected.Node.Style = unwrapDoc(candidate.Node).Style
|
||||||
|
|
||||||
|
results.PushBack(collected)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.ChildContext(results), nil
|
||||||
|
}
|
51
pkg/yqlib/operator_map_test.go
Normal file
51
pkg/yqlib/operator_map_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mapOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `[1,2,3]`,
|
||||||
|
document2: `[5,6,7]`,
|
||||||
|
expression: `map(. + 1)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!seq)::[2, 3, 4]\n",
|
||||||
|
"D0, P[], (!!seq)::[6, 7, 8]\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Map array",
|
||||||
|
document: `[1,2,3]`,
|
||||||
|
expression: `map(. + 1)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!seq)::[2, 3, 4]\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `{a: 1, b: 2, c: 3}`,
|
||||||
|
document2: `{x: 10, y: 20, z: 30}`,
|
||||||
|
expression: `map_values(. + 1)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::{a: 2, b: 3, c: 4}\n",
|
||||||
|
"D0, P[], (doc)::{x: 11, y: 21, z: 31}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Map object values",
|
||||||
|
document: `{a: 1, b: 2, c: 3}`,
|
||||||
|
expression: `map_values(. + 1)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::{a: 2, b: 3, c: 4}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapOperatorScenarios(t *testing.T) {
|
||||||
|
for _, tt := range mapOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
documentScenarios(t, "map", mapOperatorScenarios)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user