Added reverse op

This commit is contained in:
Mike Farah 2022-02-22 14:15:31 +11:00
parent 2752544bc8
commit 8142e94349
9 changed files with 176 additions and 0 deletions

View File

@ -0,0 +1,3 @@
# Reverse
Reverses the order of the items in an array

View File

@ -2,4 +2,7 @@
Sorts an array. Use `sort` to sort an array as is, or `sort_by(exp)` to sort by a particular expression (e.g. subfield).
To sort by descending order, pipe the results through the `reverse` operator after sorting.
Note that at this stage, `yq` only sorts scalar fields.

View File

@ -0,0 +1,48 @@
# Reverse
Reverses the order of the items in an array
{% hint style="warning" %}
Note that versions prior to 4.18 require the 'eval/e' command to be specified. 
`yq e <exp> <file>`
{% endhint %}
## Reverse
Given a sample.yml file of:
```yaml
- 1
- 2
- 3
```
then
```bash
yq 'reverse' sample.yml
```
will output
```yaml
- 3
- 2
- 1
```
## Sort descending by string field
Use sort with reverse to sort in descending order.
Given a sample.yml file of:
```yaml
- a: banana
- a: cat
- a: apple
```
then
```bash
yq 'sort_by(.a) | reverse' sample.yml
```
will output
```yaml
- a: cat
- a: banana
- a: apple
```

View File

@ -2,8 +2,11 @@
Sorts an array. Use `sort` to sort an array as is, or `sort_by(exp)` to sort by a particular expression (e.g. subfield).
To sort by descending order, pipe the results through the `reverse` operator after sorting.
Note that at this stage, `yq` only sorts scalar fields.
{% hint style="warning" %}
Note that versions prior to 4.18 require the 'eval/e' command to be specified.&#x20;
@ -28,6 +31,26 @@ will output
- a: cat
```
## Sort descending by string field
Use sort with reverse to sort in descending order.
Given a sample.yml file of:
```yaml
- a: banana
- a: cat
- a: apple
```
then
```bash
yq 'sort_by(.a) | reverse' sample.yml
```
will output
```yaml
- a: cat
- a: banana
- a: apple
```
## Sort array in place
Given a sample.yml file of:
```yaml

View File

@ -416,6 +416,7 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`sort`), opToken(sortOpType))
lexer.Add([]byte(`sort_by`), opToken(sortByOpType))
lexer.Add([]byte(`reverse`), opToken(reverseOpType))
lexer.Add([]byte(`any`), opToken(anyOpType))
lexer.Add([]byte(`any_c`), opToken(anyConditionOpType))

View File

@ -123,6 +123,7 @@ var getPathOpType = &operationType{Type: "GET_PATH", NumArgs: 0, Precedence: 50,
var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: explodeOperator}
var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator}
var reverseOpType = &operationType{Type: "REVERSE", NumArgs: 0, Precedence: 50, Handler: reverseOperator}
var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator}
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator}

View File

@ -0,0 +1,34 @@
package yqlib
import (
"container/list"
"fmt"
yaml "gopkg.in/yaml.v3"
)
func reverseOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
results := list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node)
if candidateNode.Kind != yaml.SequenceNode {
return context, fmt.Errorf("node at path [%v] is not an array (it's a %v)", candidate.GetNicePath(), candidate.GetNiceTag())
}
reverseList := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Style: candidateNode.Style}
reverseList.Content = make([]*yaml.Node, len(candidateNode.Content))
for i, originalNode := range candidateNode.Content {
reverseList.Content[len(candidateNode.Content)-i-1] = originalNode
}
results.PushBack(candidate.CreateReplacement(reverseList))
}
return context.ChildContext(results), nil
}

View File

@ -0,0 +1,54 @@
package yqlib
import "testing"
var reverseOperatorScenarios = []expressionScenario{
{
description: "Reverse",
document: "[1, 2, 3]",
expression: `reverse`,
expected: []string{
"D0, P[], (!!seq)::[3, 2, 1]\n",
},
},
{
skipDoc: true,
document: "[]",
expression: `reverse`,
expected: []string{
"D0, P[], (!!seq)::[]\n",
},
},
{
skipDoc: true,
document: "[1]",
expression: `reverse`,
expected: []string{
"D0, P[], (!!seq)::[1]\n",
},
},
{
skipDoc: true,
document: "[1,2]",
expression: `reverse`,
expected: []string{
"D0, P[], (!!seq)::[2, 1]\n",
},
},
{
description: "Sort descending by string field",
subdescription: "Use sort with reverse to sort in descending order.",
document: "[{a: banana},{a: cat},{a: apple}]",
expression: `sort_by(.a) | reverse`,
expected: []string{
"D0, P[], (!!seq)::[{a: cat}, {a: banana}, {a: apple}]\n",
},
},
}
func TestReverseOperatorScenarios(t *testing.T) {
for _, tt := range reverseOperatorScenarios {
testScenario(t, &tt)
}
documentOperatorScenarios(t, "reverse", reverseOperatorScenarios)
}

View File

@ -11,6 +11,15 @@ var sortByOperatorScenarios = []expressionScenario{
"D0, P[], (!!seq)::[{a: apple}, {a: banana}, {a: cat}]\n",
},
},
{
description: "Sort descending by string field",
subdescription: "Use sort with reverse to sort in descending order.",
document: "[{a: banana},{a: cat},{a: apple}]",
expression: `sort_by(.a) | reverse`,
expected: []string{
"D0, P[], (!!seq)::[{a: cat}, {a: banana}, {a: apple}]\n",
},
},
{
description: "Sort array in place",
document: "cool: [{a: banana},{a: cat},{a: apple}]",