Skip to content

Commit

Permalink
Added reverse op
Browse files Browse the repository at this point in the history
  • Loading branch information
mikefarah committed Feb 22, 2022
1 parent 2752544 commit 8142e94
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 0 deletions.
3 changes: 3 additions & 0 deletions pkg/yqlib/doc/operators/headers/reverse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Reverse

Reverses the order of the items in an array
3 changes: 3 additions & 0 deletions pkg/yqlib/doc/operators/headers/sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

48 changes: 48 additions & 0 deletions pkg/yqlib/doc/operators/reverse.md
Original file line number Diff line number Diff line change
@@ -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
```
23 changes: 23 additions & 0 deletions pkg/yqlib/doc/operators/sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions pkg/yqlib/expression_tokeniser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
1 change: 1 addition & 0 deletions pkg/yqlib/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
34 changes: 34 additions & 0 deletions pkg/yqlib/operator_reverse.go
Original file line number Diff line number Diff line change
@@ -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

}
54 changes: 54 additions & 0 deletions pkg/yqlib/operator_reverse_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
9 changes: 9 additions & 0 deletions pkg/yqlib/operator_sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}]",
Expand Down

0 comments on commit 8142e94

Please sign in to comment.