假设我们有两个json对象:{"messages":["one"], "keyA": "valueA"}
以及{"messages":["two"], "keyB": "valueB"}
我希望有一种方法可以在连接数组值的同时合并这两个对象,这样得到的对象将是:{"messages":["one","two"], "keyA": "valueA", "keyB": "valueB"}
到目前为止,我所看到的大多数方法都不足以实现这一点,因为数组会被"最右边"对象的版本覆盖。
即:echo '{"messages":["one"], "keyA": "valueA"}{"messages":["two"], "keyB": "valueB"}' | jq -s '.[0] * .[1]'
产生:
"messages": [
"two"
],
"keyA": "valueA",
"keyB": "valueB"
}
(NOTE:messages
数组值仅包含two
(来自第二个(最右侧)对象)
从jq手册上的"add"命令:
对象是通过合并添加的,即将两个对象中的所有键-值对插入到一个组合对象中。如果两个对象包含同一个键的值,则+右边的对象优先。(对于递归合并,请使用 * 运算符。)
(着重号后加)
但是将+
运算符更改为*
似乎不会更改输出。
我看过jq: recursively merge objects and concatenate arrays,但是......哇......难道没有更好的办法吗?
如果解决方案可以像处理空数组一样处理数组键值为null
的对象,则会加分:{"messages":null, "keyA": "valueA"}{"messages":["two"], "keyB": "valueB"}
2条答案
按热度按时间f2uvfpb91#
我发现了几种方法:
首先,这是一个known issue
其次,从已知问题中我发现了这一点:
jq -s '[.[] | to_entries] | flatten | reduce .[] as $dot ({}; .[$dot.key] += $dot.value)'
例如:
echo '{"messages":["one"], "keyA": "valueA"}{"messages":["two"], "keyB": "valueB"}' | jq -s '[.[] | to_entries] | flatten | reduce .[] as $dot ({}; .[$dot.key] += $dot.value)'
产生:
(as期望值)
这来自https://github.com/stedolan/jq/issues/502
此外,受https://github.com/stedolan/jq/issues/957的启发,我能够做到这一点:
echo '{"messages":["one"], "keyA": "valueA"}{"messages":["two"], "keyB": "valueB"}' | jq -s '.[2].messages = .[0].messages + .[1].messages | .[0] + .[1] + .[2]'
这也会产生预期的输出,但是是以一种非一般化的方式。将所需的合并数组存储在根数组的第三个元素中的灵感非常简洁(这就是我提到它的原因);则使用现有的"右侧获胜"行为将正确合并的数组插回到结果中。
最后,从同一个问题,有这样的:
echo '{"messages":["one"], "keyA": "valueA"}{"messages":["two"], "keyB": "valueB"}' | jq -s '.[0] as $o1 | .[1] as $o2 | ($o1 + $o2) | .messages = ($o1.messages + $o2.messages)'
其本质上是与上述临时存储方法相同的非一般化解决方案(但可能更优雅?)。
ijxebb2r2#
对于问题中的简单示例,以下筛选器提供了一个简单但有原则的方法,该方法也符合“奖励积分”的条件:
当然,这是不可交换的。
对于两个以上的对象,只需使用
reduce
,例如,对于对象数组: