mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-14 15:18:06 +00:00
Added from_entries op
This commit is contained in:
parent
77630ca179
commit
941a453163
@ -35,3 +35,37 @@ will output
|
|||||||
value: b
|
value: b
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## from_entries map
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: 1
|
||||||
|
b: 2
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval 'to_entries | from_entries' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: 1
|
||||||
|
b: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
## from_entries with numeric key indexes
|
||||||
|
from_entries always creates a map, even for numeric keys
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval 'to_entries | from_entries' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
0: a
|
||||||
|
1: b
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -279,6 +279,7 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`fi`), opToken(getFileIndexOpType))
|
lexer.Add([]byte(`fi`), opToken(getFileIndexOpType))
|
||||||
lexer.Add([]byte(`path`), opToken(getPathOpType))
|
lexer.Add([]byte(`path`), opToken(getPathOpType))
|
||||||
lexer.Add([]byte(`to_entries`), opToken(toEntriesOpType))
|
lexer.Add([]byte(`to_entries`), opToken(toEntriesOpType))
|
||||||
|
lexer.Add([]byte(`from_entries`), opToken(fromEntriesOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`lineComment`), opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{LineComment: true}))
|
lexer.Add([]byte(`lineComment`), opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{LineComment: true}))
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ 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: 0, Precedence: 50, Handler: collectOperator}
|
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
|
||||||
var toEntriesOpType = &operationType{Type: "TO_ENTRIES", NumArgs: 0, Precedence: 50, Handler: toEntriesOperator}
|
var toEntriesOpType = &operationType{Type: "TO_ENTRIES", NumArgs: 0, Precedence: 50, Handler: toEntriesOperator}
|
||||||
|
var fromEntriesOpType = &operationType{Type: "TO_ENTRIES", NumArgs: 0, Precedence: 50, Handler: fromEntriesOperator}
|
||||||
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
|
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
|
||||||
var getVariableOpType = &operationType{Type: "GET_VARIABLE", NumArgs: 0, Precedence: 55, Handler: getVariableOperator}
|
var getVariableOpType = &operationType{Type: "GET_VARIABLE", NumArgs: 0, Precedence: 55, Handler: getVariableOperator}
|
||||||
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
|
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
|
||||||
|
@ -63,5 +63,66 @@ func toEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
return context.ChildContext(results), nil
|
return context.ChildContext(results), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEntry(d *dataTreeNavigator, entry *yaml.Node, position int) (*yaml.Node, *yaml.Node, error) {
|
||||||
|
prefs := traversePreferences{DontAutoCreate: true}
|
||||||
|
candidateNode := &CandidateNode{Node: entry}
|
||||||
|
|
||||||
|
keyResults, err := traverseMap(Context{}, candidateNode, "key", prefs, false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if keyResults.Len() != 1 {
|
||||||
|
return nil, nil, fmt.Errorf("Expected to find one 'key' entry but found %v in position %v", keyResults.Len(), position)
|
||||||
|
}
|
||||||
|
|
||||||
|
valueResults, err := traverseMap(Context{}, candidateNode, "value", prefs, false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if valueResults.Len() != 1 {
|
||||||
|
return nil, nil, fmt.Errorf("Expected to find one 'value' entry but found %v in position %v", valueResults.Len(), position)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyResults.Front().Value.(*CandidateNode).Node, valueResults.Front().Value.(*CandidateNode).Node, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fromEntries(d *dataTreeNavigator, candidateNode * CandidateNode) (*CandidateNode, error) {
|
||||||
|
var node = &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
||||||
|
var mapCandidateNode = candidateNode.CreateChild(nil, node)
|
||||||
|
|
||||||
|
var contents = unwrapDoc(candidateNode.Node).Content
|
||||||
|
|
||||||
|
for index := 0; index < len(contents); index = index + 1 {
|
||||||
|
key, value, err := parseEntry(d, contents[index], index)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Content = append(node.Content, key, value)
|
||||||
|
}
|
||||||
|
return mapCandidateNode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromEntriesOperator(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)
|
||||||
|
candidateNode := unwrapDoc(candidate.Node)
|
||||||
|
|
||||||
|
switch candidateNode.Kind {
|
||||||
|
case yaml.SequenceNode:
|
||||||
|
mapResult, err :=fromEntries(d, candidate)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
results.PushBack(mapResult)
|
||||||
|
default:
|
||||||
|
return Context{}, fmt.Errorf("from entries only runs against arrays")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.ChildContext(results), nil
|
||||||
|
}
|
@ -21,6 +21,23 @@ var entriesOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!seq)::- key: 0\n value: a\n- key: 1\n value: b\n",
|
"D0, P[], (!!seq)::- key: 0\n value: a\n- key: 1\n value: b\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "from_entries map",
|
||||||
|
document: `{a: 1, b: 2}`,
|
||||||
|
expression: `to_entries | from_entries`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::a: 1\nb: 2\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "from_entries with numeric key indexes",
|
||||||
|
subdescription: "from_entries always creates a map, even for numeric keys",
|
||||||
|
document: `[a,b]`,
|
||||||
|
expression: `to_entries | from_entries`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::0: a\n1: b\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEntriesOperatorScenarios(t *testing.T) {
|
func TestEntriesOperatorScenarios(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user