mirror of
https://github.com/mikefarah/yq.git
synced 2026-07-07 06:10:36 +00:00
Added support for adding objects
This commit is contained in:
parent
9b66e0a094
commit
9b1a7bf451
@ -4,10 +4,10 @@ Add behaves differently according to the type of the LHS:
|
|||||||
* arrays: concatenate
|
* arrays: concatenate
|
||||||
* number scalars: arithmetic addition
|
* number scalars: arithmetic addition
|
||||||
* string scalars: concatenate
|
* string scalars: concatenate
|
||||||
|
* maps: shallow merge (use the multiply operator (`*`) to deeply merge)
|
||||||
|
|
||||||
Use `+=` as append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`.
|
Use `+=` as a relative append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`.
|
||||||
|
|
||||||
Add is not (yet) supported for maps - however you can use merge `*` which will have a similar effect...
|
|
||||||
|
|
||||||
## Concatenate and assign arrays
|
## Concatenate and assign arrays
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
@ -267,6 +267,41 @@ will output
|
|||||||
cat
|
cat
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Add maps to shallow merge
|
||||||
|
Adding objects together shallow merges them. Use `*` to deeply merge.
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
thing:
|
||||||
|
name: Astuff
|
||||||
|
value: x
|
||||||
|
a1: cool
|
||||||
|
b:
|
||||||
|
thing:
|
||||||
|
name: Bstuff
|
||||||
|
legs: 3
|
||||||
|
b1: neat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a += .b' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
thing:
|
||||||
|
name: Bstuff
|
||||||
|
legs: 3
|
||||||
|
a1: cool
|
||||||
|
b1: neat
|
||||||
|
b:
|
||||||
|
thing:
|
||||||
|
name: Bstuff
|
||||||
|
legs: 3
|
||||||
|
b1: neat
|
||||||
|
```
|
||||||
|
|
||||||
## Custom types: that are really strings
|
## Custom types: that are really strings
|
||||||
When custom tags are encountered, yq will try to decode the underlying type.
|
When custom tags are encountered, yq will try to decode the underlying type.
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ Add behaves differently according to the type of the LHS:
|
|||||||
* arrays: concatenate
|
* arrays: concatenate
|
||||||
* number scalars: arithmetic addition
|
* number scalars: arithmetic addition
|
||||||
* string scalars: concatenate
|
* string scalars: concatenate
|
||||||
|
* maps: shallow merge (use the multiply operator (`*`) to deeply merge)
|
||||||
|
|
||||||
Use `+=` as append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`.
|
Use `+=` as a relative append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`.
|
||||||
|
|
||||||
Add is not (yet) supported for maps - however you can use merge `*` which will have a similar effect...
|
|
||||||
|
|||||||
@ -52,21 +52,19 @@ func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *Candida
|
|||||||
|
|
||||||
switch lhsNode.Kind {
|
switch lhsNode.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
return nil, fmt.Errorf("maps not yet supported for addition")
|
addMaps(target, lhs, rhs)
|
||||||
case yaml.SequenceNode:
|
case yaml.SequenceNode:
|
||||||
target.Node.Kind = yaml.SequenceNode
|
addSequences(target, lhs, rhs)
|
||||||
target.Node.Style = lhsNode.Style
|
|
||||||
target.Node.Tag = lhsNode.Tag
|
|
||||||
target.Node.Content = append(lhsNode.Content, toNodes(rhs)...)
|
|
||||||
case yaml.ScalarNode:
|
case yaml.ScalarNode:
|
||||||
if rhs.Node.Kind != yaml.ScalarNode {
|
if rhs.Node.Kind != yaml.ScalarNode {
|
||||||
return nil, fmt.Errorf("%v (%v) cannot be added to a 2%v", rhs.Node.Tag, rhs.Path, lhsNode.Tag)
|
return nil, fmt.Errorf("%v (%v) cannot be added to a %v", rhs.Node.Tag, rhs.Path, lhsNode.Tag)
|
||||||
}
|
}
|
||||||
target.Node.Kind = yaml.ScalarNode
|
target.Node.Kind = yaml.ScalarNode
|
||||||
target.Node.Style = lhsNode.Style
|
target.Node.Style = lhsNode.Style
|
||||||
return addScalars(target, lhsNode, rhs.Node)
|
if err := addScalars(target, lhsNode, rhs.Node); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +87,7 @@ func guessTagFromCustomType(node *yaml.Node) string {
|
|||||||
return guessedTag
|
return guessedTag
|
||||||
}
|
}
|
||||||
|
|
||||||
func addScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) (*CandidateNode, error) {
|
func addScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) error {
|
||||||
lhsTag := lhs.Tag
|
lhsTag := lhs.Tag
|
||||||
rhsTag := rhs.Tag
|
rhsTag := rhs.Tag
|
||||||
lhsIsCustom := false
|
lhsIsCustom := false
|
||||||
@ -110,11 +108,11 @@ func addScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) (*Candida
|
|||||||
} else if lhsTag == "!!int" && rhsTag == "!!int" {
|
} else if lhsTag == "!!int" && rhsTag == "!!int" {
|
||||||
format, lhsNum, err := parseInt(lhs.Value)
|
format, lhsNum, err := parseInt(lhs.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
_, rhsNum, err := parseInt(rhs.Value)
|
_, rhsNum, err := parseInt(rhs.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
sum := lhsNum + rhsNum
|
sum := lhsNum + rhsNum
|
||||||
target.Node.Tag = lhs.Tag
|
target.Node.Tag = lhs.Tag
|
||||||
@ -122,11 +120,11 @@ func addScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) (*Candida
|
|||||||
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
|
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
|
||||||
lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
|
lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
rhsNum, err := strconv.ParseFloat(rhs.Value, 64)
|
rhsNum, err := strconv.ParseFloat(rhs.Value, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
sum := lhsNum + rhsNum
|
sum := lhsNum + rhsNum
|
||||||
if lhsIsCustom {
|
if lhsIsCustom {
|
||||||
@ -136,8 +134,42 @@ func addScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) (*Candida
|
|||||||
}
|
}
|
||||||
target.Node.Value = fmt.Sprintf("%v", sum)
|
target.Node.Value = fmt.Sprintf("%v", sum)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("%v cannot be added to %v", lhsTag, rhsTag)
|
return fmt.Errorf("%v cannot be added to %v", lhsTag, rhsTag)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return target, nil
|
}
|
||||||
|
|
||||||
|
func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) {
|
||||||
|
target.Node.Kind = yaml.SequenceNode
|
||||||
|
target.Node.Style = lhs.Node.Style
|
||||||
|
target.Node.Tag = lhs.Node.Tag
|
||||||
|
target.Node.Content = make([]*yaml.Node, len(lhs.Node.Content))
|
||||||
|
copy(target.Node.Content, lhs.Node.Content)
|
||||||
|
target.Node.Content = append(target.Node.Content, toNodes(rhs)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addMaps(target *CandidateNode, lhsC *CandidateNode, rhsC *CandidateNode) {
|
||||||
|
lhs := lhsC.Node
|
||||||
|
rhs := rhsC.Node
|
||||||
|
|
||||||
|
target.Node.Content = make([]*yaml.Node, len(lhs.Content))
|
||||||
|
copy(target.Node.Content, lhs.Content)
|
||||||
|
|
||||||
|
for index := 0; index < len(rhs.Content); index = index + 2 {
|
||||||
|
key := rhs.Content[index]
|
||||||
|
value := rhs.Content[index+1]
|
||||||
|
log.Debug("finding %v", key.Value)
|
||||||
|
indexInLhs := findInArray(target.Node, key)
|
||||||
|
log.Debug("indexInLhs %v", indexInLhs)
|
||||||
|
if indexInLhs < 0 {
|
||||||
|
// not in there, append it
|
||||||
|
target.Node.Content = append(target.Node.Content, key, value)
|
||||||
|
} else {
|
||||||
|
// it's there, replace it
|
||||||
|
target.Node.Content[indexInLhs+1] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.Node.Kind = yaml.MappingNode
|
||||||
|
target.Node.Style = lhs.Style
|
||||||
|
target.Node.Tag = lhs.Tag
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,6 +144,15 @@ var addOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!str)::cat\n",
|
"D0, P[], (!!str)::cat\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Add maps to shallow merge",
|
||||||
|
subdescription: "Adding objects together shallow merges them. Use `*` to deeply merge.",
|
||||||
|
document: "a: {thing: {name: Astuff, value: x}, a1: cool}\nb: {thing: {name: Bstuff, legs: 3}, b1: neat}",
|
||||||
|
expression: `.a += .b`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: {thing: {name: Bstuff, legs: 3}, a1: cool, b1: neat}\nb: {thing: {name: Bstuff, legs: 3}, b1: neat}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Custom types: that are really strings",
|
description: "Custom types: that are really strings",
|
||||||
subdescription: "When custom tags are encountered, yq will try to decode the underlying type.",
|
subdescription: "When custom tags are encountered, yq will try to decode the underlying type.",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user