Array to map operator for #1415

This commit is contained in:
Mike Farah 2022-11-08 13:40:00 +11:00
parent c915113120
commit bd5e5dc965
8 changed files with 113 additions and 0 deletions

View File

@ -0,0 +1,28 @@
# Array to Map
Use this operator to convert an array to..a map. Skips over null values.
Behind the scenes, this is implemented using reduce:
```
(.[] | select(. != null) ) as $i ireduce({}; .[$i | key] = $i)
```
## Simple example
Given a sample.yml file of:
```yaml
cool:
- null
- null
- hello
```
then
```bash
yq '.cool |= array_to_map' sample.yml
```
will output
```yaml
cool:
2: hello
```

View File

@ -0,0 +1,9 @@
# Array to Map
Use this operator to convert an array to..a map. Skips over null values.
Behind the scenes, this is implemented using reduce:
```
(.[] | select(. != null) ) as $i ireduce({}; .[$i | key] = $i)
```

View File

@ -149,6 +149,23 @@ person:
- pizza
```
## Decode properties - array should be a map
If you have a numeric map key in your property files, use array_to_map to convert them to maps.
Given a sample.properties file of:
```properties
things.10 = mike
```
then
```bash
yq -p=props '.things |= array_to_map' sample.properties
```
will output
```yaml
things:
10: mike
```
## Roundtrip
Given a sample.properties file of:
```properties

View File

@ -55,6 +55,8 @@ var participleYqRules = []*participleYqRule{
simpleOp("sortKeys", sortKeysOpType),
simpleOp("sort_?keys", sortKeysOpType),
{"ArrayToMap", "array_?to_?map", expressionOpToken(`(.[] | select(. != null) ) as $i ireduce({}; .[$i | key] = $i)`), 0},
{"YamlEncodeWithIndent", `to_?yaml\([0-9]+\)`, encodeParseIndent(YamlOutputFormat), 0},
{"XMLEncodeWithIndent", `to_?xml\([0-9]+\)`, encodeParseIndent(XMLOutputFormat), 0},
{"JSONEncodeWithIndent", `to_?json\([0-9]+\)`, encodeParseIndent(JSONOutputFormat), 0},
@ -291,6 +293,14 @@ func opTokenWithPrefs(opType *operationType, assignOpType *operationType, prefer
}
}
func expressionOpToken(expression string) yqAction {
return func(rawToken lexer.Token) (*token, error) {
prefs := expressionOpPreferences{expression: expression}
expressionOp := &Operation{OperationType: expressionOpType, Preferences: prefs}
return &token{TokenType: operationToken, Operation: expressionOp}, nil
}
}
func flattenWithDepth() yqAction {
return func(rawToken lexer.Token) (*token, error) {
value := rawToken.Value

View File

@ -80,6 +80,8 @@ var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Ha
var lineOpType = &operationType{Type: "LINE", NumArgs: 0, Precedence: 50, Handler: lineOperator}
var columnOpType = &operationType{Type: "LINE", NumArgs: 0, Precedence: 50, Handler: columnOperator}
var expressionOpType = &operationType{Type: "EXP", NumArgs: 0, Precedence: 50, Handler: expressionOperator}
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
var sliceArrayOpType = &operationType{Type: "SLICE", NumArgs: 0, Precedence: 50, Handler: sliceArrayOperator}
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}

View File

@ -0,0 +1,23 @@
package yqlib
import (
"testing"
)
var arrayToMapScenarios = []expressionScenario{
{
description: "Simple example",
document: `cool: [null, null, hello]`,
expression: `.cool |= array_to_map`,
expected: []string{
"D0, P[], (doc)::cool:\n 2: hello\n",
},
},
}
func TestArrayToMapScenarios(t *testing.T) {
for _, tt := range arrayToMapScenarios {
testScenario(t, &tt)
}
documentOperatorScenarios(t, "array-to-map", arrayToMapScenarios)
}

View File

@ -0,0 +1,16 @@
package yqlib
type expressionOpPreferences struct {
expression string
}
func expressionOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
prefs := expressionNode.Operation.Preferences.(expressionOpPreferences)
expNode, err := ExpressionParser.ParseExpression(prefs.expression)
if err != nil {
return Context{}, err
}
return d.GetMatchingNodes(context, expNode)
}

View File

@ -147,6 +147,14 @@ var propertyScenarios = []formatScenario{
expected: expectedDecodedYaml,
scenarioType: "decode",
},
{
description: "Decode properties - array should be a map",
subdescription: "If you have a numeric map key in your property files, use array_to_map to convert them to maps.",
input: `things.10 = mike`,
expression: `.things |= array_to_map`,
expected: "things:\n 10: mike\n",
scenarioType: "decode",
},
{
description: "does not expand automatically",
skipDoc: true,