mirror of
https://github.com/mikefarah/yq.git
synced 2025-01-23 14:16:10 +00:00
sort_by works on maps
This commit is contained in:
parent
1508f1fd5f
commit
f202d06d82
@ -1,11 +1,3 @@
|
|||||||
# block comments come through
|
Foo: 3
|
||||||
person: # neither do comments on maps
|
apple: 1
|
||||||
name: Mike Wazowski # comments on values appear
|
bar: 2
|
||||||
pets:
|
|
||||||
- cat # comments on array values appear
|
|
||||||
- dog # comments on array values appear
|
|
||||||
- things:
|
|
||||||
- frog
|
|
||||||
food: [pizza] # comments on arrays do not
|
|
||||||
emptyArray: []
|
|
||||||
emptyMap: []
|
|
4
go.mod
4
go.mod
@ -29,6 +29,4 @@ require (
|
|||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.21.0
|
go 1.23.0
|
||||||
|
|
||||||
toolchain go1.22.5
|
|
||||||
|
@ -198,6 +198,29 @@ func (n *CandidateNode) SetParent(parent *CandidateNode) {
|
|||||||
n.Parent = parent
|
n.Parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ValueVisitor func(*CandidateNode) error
|
||||||
|
|
||||||
|
func (n *CandidateNode) VisitValues(visitor ValueVisitor) error {
|
||||||
|
if n.Kind == MappingNode {
|
||||||
|
for i := 1; i < len(n.Content); i = i + 2 {
|
||||||
|
if err := visitor(n.Content[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if n.Kind == SequenceNode {
|
||||||
|
for i := 0; i < len(n.Content); i = i + 1 {
|
||||||
|
if err := visitor(n.Content[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *CandidateNode) CanVisitValues() bool {
|
||||||
|
return n.Kind == MappingNode || n.Kind == SequenceNode
|
||||||
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) AddKeyValueChild(rawKey *CandidateNode, rawValue *CandidateNode) (*CandidateNode, *CandidateNode) {
|
func (n *CandidateNode) AddKeyValueChild(rawKey *CandidateNode, rawValue *CandidateNode) (*CandidateNode, *CandidateNode) {
|
||||||
key := rawKey.Copy()
|
key := rawKey.Copy()
|
||||||
key.SetParent(n)
|
key.SetParent(n)
|
||||||
|
@ -12,5 +12,5 @@ diff file1.yml file2.yml
|
|||||||
|
|
||||||
Note that `yq` does not yet consider anchors when sorting by keys - this may result in invalid yaml documents if you are using merge anchors.
|
Note that `yq` does not yet consider anchors when sorting by keys - this may result in invalid yaml documents if you are using merge anchors.
|
||||||
|
|
||||||
For more advanced sorting, using `to_entries` to convert the map to an array, then sort/process the array as you like (e.g. using `sort_by`) and convert back to a map using `from_entries`.
|
For more advanced sorting, you can use the [sort_by](https://mikefarah.gitbook.io/yq/operators/sort) function on a map, and give it a custom function like `sort_by(key | downcase)`.
|
||||||
See [here](https://mikefarah.gitbook.io/yq/operators/entries#custom-sort-map-keys) for an example.
|
|
||||||
|
@ -12,8 +12,8 @@ diff file1.yml file2.yml
|
|||||||
|
|
||||||
Note that `yq` does not yet consider anchors when sorting by keys - this may result in invalid yaml documents if you are using merge anchors.
|
Note that `yq` does not yet consider anchors when sorting by keys - this may result in invalid yaml documents if you are using merge anchors.
|
||||||
|
|
||||||
For more advanced sorting, using `to_entries` to convert the map to an array, then sort/process the array as you like (e.g. using `sort_by`) and convert back to a map using `from_entries`.
|
For more advanced sorting, you can use the [sort_by](https://mikefarah.gitbook.io/yq/operators/sort) function on a map, and give it a custom function like `sort_by(key | downcase)`.
|
||||||
See [here](https://mikefarah.gitbook.io/yq/operators/entries#custom-sort-map-keys) for an example.
|
|
||||||
|
|
||||||
## Sort keys of map
|
## Sort keys of map
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
|
@ -109,6 +109,46 @@ cool:
|
|||||||
- c: banana
|
- c: banana
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Sort a map
|
||||||
|
Sorting a map, by default, will sort by the values
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
y: b
|
||||||
|
z: a
|
||||||
|
x: c
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq 'sort' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
z: a
|
||||||
|
y: b
|
||||||
|
x: c
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sort a map by keys
|
||||||
|
Use sort_by to sort a map using a custom function
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
Y: b
|
||||||
|
z: a
|
||||||
|
x: c
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq 'sort_by(key | downcase)' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
x: c
|
||||||
|
Y: b
|
||||||
|
z: a
|
||||||
|
```
|
||||||
|
|
||||||
## Sort is stable
|
## Sort is stable
|
||||||
Note the order of the elements in unchanged when equal in sorting.
|
Note the order of the elements in unchanged when equal in sorting.
|
||||||
|
|
||||||
|
@ -24,30 +24,40 @@ func sortByOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
|
|||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
if candidate.Kind != SequenceNode {
|
var sortableArray sortableNodeArray
|
||||||
return context, fmt.Errorf("node at path [%v] is not an array (it's a %v)", candidate.GetNicePath(), candidate.Tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
sortableArray := make(sortableNodeArray, len(candidate.Content))
|
if candidate.CanVisitValues() {
|
||||||
|
sortableArray = make(sortableNodeArray, 0)
|
||||||
for i, originalNode := range candidate.Content {
|
visitor := func(valueNode *CandidateNode) error {
|
||||||
|
compareContext, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(valueNode), expressionNode.RHS)
|
||||||
compareContext, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(originalNode), expressionNode.RHS)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return Context{}, err
|
}
|
||||||
|
sortableNode := sortableNode{Node: valueNode, CompareContext: compareContext, dateTimeLayout: context.GetDateTimeLayout()}
|
||||||
|
sortableArray = append(sortableArray, sortableNode)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
if err := candidate.VisitValues(visitor); err != nil {
|
||||||
sortableArray[i] = sortableNode{Node: originalNode, CompareContext: compareContext, dateTimeLayout: context.GetDateTimeLayout()}
|
return context, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return context, fmt.Errorf("node at path [%v] is not an array or map (it's a %v)", candidate.GetNicePath(), candidate.Tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Stable(sortableArray)
|
sort.Stable(sortableArray)
|
||||||
|
|
||||||
sortedList := candidate.CreateReplacementWithComments(SequenceNode, "!!seq", candidate.Style)
|
sortedList := candidate.CopyWithoutContent()
|
||||||
|
if candidate.Kind == MappingNode {
|
||||||
for _, sortedNode := range sortableArray {
|
for _, sortedNode := range sortableArray {
|
||||||
sortedList.AddChild(sortedNode.Node)
|
sortedList.AddKeyValueChild(sortedNode.Node.Key, sortedNode.Node)
|
||||||
|
}
|
||||||
|
} else if candidate.Kind == SequenceNode {
|
||||||
|
for _, sortedNode := range sortableArray {
|
||||||
|
sortedList.AddChild(sortedNode.Node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert array of value nodes back to map
|
||||||
results.PushBack(sortedList)
|
results.PushBack(sortedList)
|
||||||
}
|
}
|
||||||
return context.ChildContext(results), nil
|
return context.ChildContext(results), nil
|
||||||
|
@ -84,6 +84,24 @@ var sortByOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!map)::cool: [{a: banana}, {b: banana}, {c: banana}]\n",
|
"D0, P[], (!!map)::cool: [{a: banana}, {b: banana}, {c: banana}]\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Sort a map",
|
||||||
|
subdescription: "Sorting a map, by default this will sort by the values",
|
||||||
|
document: "y: b\nz: a\nx: c\n",
|
||||||
|
expression: `sort`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::z: a\ny: b\nx: c\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Sort a map by keys",
|
||||||
|
subdescription: "Use sort_by to sort a map using a custom function",
|
||||||
|
document: "Y: b\nz: a\nx: c\n",
|
||||||
|
expression: `sort_by(key | downcase)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::x: c\nY: b\nz: a\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Sort is stable",
|
description: "Sort is stable",
|
||||||
subdescription: "Note the order of the elements in unchanged when equal in sorting.",
|
subdescription: "Note the order of the elements in unchanged when equal in sorting.",
|
||||||
|
Loading…
Reference in New Issue
Block a user