mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
Added shuffle command #1503
This commit is contained in:
parent
a1698b740a
commit
d17fd9424e
4
pkg/yqlib/doc/operators/headers/shuffle.md
Normal file
4
pkg/yqlib/doc/operators/headers/shuffle.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Shuffle
|
||||||
|
|
||||||
|
Shuffles an array. Note that this command does _not_ use a cryptographically secure random number generator to randomise the array order.
|
||||||
|
|
51
pkg/yqlib/doc/operators/shuffle.md
Normal file
51
pkg/yqlib/doc/operators/shuffle.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Shuffle
|
||||||
|
|
||||||
|
Shuffles an array. Note that this command does _not_ use a cryptographically secure random number generator to randomise the array order.
|
||||||
|
|
||||||
|
|
||||||
|
## Shuffle array
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
- 5
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq 'shuffle' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
- 5
|
||||||
|
- 2
|
||||||
|
- 4
|
||||||
|
- 1
|
||||||
|
- 3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shuffle array in place
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
cool:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
- 5
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq '.cool |= shuffle' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
cool:
|
||||||
|
- 5
|
||||||
|
- 2
|
||||||
|
- 4
|
||||||
|
- 1
|
||||||
|
- 3
|
||||||
|
```
|
||||||
|
|
@ -49,6 +49,7 @@ var participleYqRules = []*participleYqRule{
|
|||||||
simpleOp("to_?unix", toUnixOpType),
|
simpleOp("to_?unix", toUnixOpType),
|
||||||
simpleOp("with_dtf", withDtFormatOpType),
|
simpleOp("with_dtf", withDtFormatOpType),
|
||||||
simpleOp("error", errorOpType),
|
simpleOp("error", errorOpType),
|
||||||
|
simpleOp("shuffle", shuffleOpType),
|
||||||
simpleOp("sortKeys", sortKeysOpType),
|
simpleOp("sortKeys", sortKeysOpType),
|
||||||
simpleOp("sort_?keys", sortKeysOpType),
|
simpleOp("sort_?keys", sortKeysOpType),
|
||||||
|
|
||||||
|
@ -135,6 +135,7 @@ var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50,
|
|||||||
var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator}
|
var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator}
|
||||||
var reverseOpType = &operationType{Type: "REVERSE", NumArgs: 0, Precedence: 50, Handler: reverseOperator}
|
var reverseOpType = &operationType{Type: "REVERSE", NumArgs: 0, Precedence: 50, Handler: reverseOperator}
|
||||||
var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator}
|
var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator}
|
||||||
|
var shuffleOpType = &operationType{Type: "SHUFFLE", NumArgs: 0, Precedence: 50, Handler: shuffleOperator}
|
||||||
|
|
||||||
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
|
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
|
||||||
|
|
||||||
|
37
pkg/yqlib/operator_shuffle.go
Normal file
37
pkg/yqlib/operator_shuffle.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func shuffleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
|
// ignore CWE-338 gosec issue of not using crypto/rand
|
||||||
|
// this is just to shuffle an array rather generating a
|
||||||
|
// secret or something that needs proper rand.
|
||||||
|
myRand := rand.New(rand.NewSource(Now().UnixNano())) // #nosec
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
result := deepClone(candidateNode)
|
||||||
|
|
||||||
|
a := result.Content
|
||||||
|
|
||||||
|
myRand.Shuffle(len(a), func(i, j int) { a[i], a[j] = a[j], a[i] })
|
||||||
|
results.PushBack(candidate.CreateReplacement(result))
|
||||||
|
}
|
||||||
|
return context.ChildContext(results), nil
|
||||||
|
}
|
30
pkg/yqlib/operator_shuffle_test.go
Normal file
30
pkg/yqlib/operator_shuffle_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var shuffleOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
description: "Shuffle array",
|
||||||
|
document: "[1, 2, 3, 4, 5]",
|
||||||
|
expression: `shuffle`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!seq)::[5, 2, 4, 1, 3]\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
description: "Shuffle array in place",
|
||||||
|
document: "cool: [1, 2, 3, 4, 5]",
|
||||||
|
expression: `.cool |= shuffle`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::cool: [5, 2, 4, 1, 3]\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShuffleByOperatorScenarios(t *testing.T) {
|
||||||
|
for _, tt := range shuffleOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
documentOperatorScenarios(t, "shuffle", shuffleOperatorScenarios)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user