如何使用 jq 合并和聚合 2 JSON 文件中的值?
How to merge and aggregate values in 2 JSON files using jq?
我在 shell 脚本中使用 jq
来操作 JSON 个文件。
我有 2 个文件,我想将它们合并到一个文件中,同时在 name/value 对中的名称相同时聚合(求和)值。
举个例子:
Input1.json
[
{
"A": "Name 1",
"B": "1.1",
"C": "2"
},
{
"A": "Name 2",
"B": "3.2",
"C": "4"
}
]
Input2.json
[
{
"A": "Name 2",
"B": "5",
"C": "6"
},
{
"A": "Name 3",
"B": "7",
"C": "8"
}
]
预期结果:
Output.json
[
{
"A": "Name 1",
"B": "1.1",
"C": "2"
},
{
"A": "Name 2",
"B": "8.2",
"C": "10"
},
{
"A": "Name 3",
"B": "7",
"C": "8"
}
]
我可以使用 jq
以外的其他工具,但更愿意最终将解决方案包含在我可以从终端调用的 shell 脚本中。
感谢任何帮助。谢谢。
这是一种方法,但还有其他方法:
jq -s '
def to_n: tonumber? // null;
def merge_values($x;$y):
if $x == $y then $x
elif $x == null then $y
elif $y == null then $x
else ($x|to_n) as $xn
| if $xn then ($y|to_n) as $yn | ($xn+$yn)|tostring
else [$x, $y]
end
end;
def merge($x;$y):
reduce ($x + $y |keys_unsorted)[] as $k (null;
.[$k] = merge_values($x[$k]; $y[$k]) );
INDEX(.[0][]; .A) as $in1
| INDEX(.[1][]; .A) as $in2
| ($in1 + $in2|keys_unsorted) as $keys
| reduce $keys[] as $k ([];
. + [merge($in1[$k]; $in2[$k]) ])
' input1.json inut2.json
I can use other tools other than jq
but prefer to ultimately keep the solution contained into a shell script I can call from the Terminal.
您可以试试 JSON 解析器 xidel:
$ xidel -se '
array{
let $src:=(json-doc("Input1.json")(),json-doc("Input2.json")())
for $name in distinct-values($src/A)
let $obj:=$src[A=$name]
return
if (count($obj) gt 1) then
map:merge(
$obj[1]() ! {
.:if ($obj[1](.) castable as decimal) then
string($obj[1](.) + $obj[2](.))
else
$obj[1](.)
}
)
else
$obj
}
'
jq 对于这样的问题很漂亮:
$ jq -n '
reduce inputs[] as {$A,$B,$C} ({};
.[$A] |= {
$A,
B: (.B + ($B|tonumber)),
C: (.C + ($C|tonumber))
}
)
| map({
A,
B: (.B|tostring),
C: (.C|tostring)
})
' input1.json input2.json
第一个 reduce 创建一个从不同“A”值到聚合结果对象的映射。然后根据映射,转换回调整结果类型的结果对象数组。
我在 shell 脚本中使用 jq
来操作 JSON 个文件。
我有 2 个文件,我想将它们合并到一个文件中,同时在 name/value 对中的名称相同时聚合(求和)值。
举个例子:
Input1.json
[
{
"A": "Name 1",
"B": "1.1",
"C": "2"
},
{
"A": "Name 2",
"B": "3.2",
"C": "4"
}
]
Input2.json
[
{
"A": "Name 2",
"B": "5",
"C": "6"
},
{
"A": "Name 3",
"B": "7",
"C": "8"
}
]
预期结果:
Output.json
[
{
"A": "Name 1",
"B": "1.1",
"C": "2"
},
{
"A": "Name 2",
"B": "8.2",
"C": "10"
},
{
"A": "Name 3",
"B": "7",
"C": "8"
}
]
我可以使用 jq
以外的其他工具,但更愿意最终将解决方案包含在我可以从终端调用的 shell 脚本中。
感谢任何帮助。谢谢。
这是一种方法,但还有其他方法:
jq -s '
def to_n: tonumber? // null;
def merge_values($x;$y):
if $x == $y then $x
elif $x == null then $y
elif $y == null then $x
else ($x|to_n) as $xn
| if $xn then ($y|to_n) as $yn | ($xn+$yn)|tostring
else [$x, $y]
end
end;
def merge($x;$y):
reduce ($x + $y |keys_unsorted)[] as $k (null;
.[$k] = merge_values($x[$k]; $y[$k]) );
INDEX(.[0][]; .A) as $in1
| INDEX(.[1][]; .A) as $in2
| ($in1 + $in2|keys_unsorted) as $keys
| reduce $keys[] as $k ([];
. + [merge($in1[$k]; $in2[$k]) ])
' input1.json inut2.json
I can use other tools other than
jq
but prefer to ultimately keep the solution contained into a shell script I can call from the Terminal.
您可以试试 JSON 解析器 xidel:
$ xidel -se '
array{
let $src:=(json-doc("Input1.json")(),json-doc("Input2.json")())
for $name in distinct-values($src/A)
let $obj:=$src[A=$name]
return
if (count($obj) gt 1) then
map:merge(
$obj[1]() ! {
.:if ($obj[1](.) castable as decimal) then
string($obj[1](.) + $obj[2](.))
else
$obj[1](.)
}
)
else
$obj
}
'
jq 对于这样的问题很漂亮:
$ jq -n '
reduce inputs[] as {$A,$B,$C} ({};
.[$A] |= {
$A,
B: (.B + ($B|tonumber)),
C: (.C + ($C|tonumber))
}
)
| map({
A,
B: (.B|tostring),
C: (.C|tostring)
})
' input1.json input2.json
第一个 reduce 创建一个从不同“A”值到聚合结果对象的映射。然后根据映射,转换回调整结果类型的结果对象数组。