如何使用JQ无需硬编码键合并数组中的所有条目?
动态键的JSON数组对象合并方法
需求说明
给定JSON数组:
[ { "name": "hosts", "ipaddress": "1.2.3.4", "status": "UP", "randomkey": "randomvalue" }, { "name": "hosts", "ipaddress": "5.6.7.8", "status": "DOWN", "newkey": "newvalue" }, { "name": "hosts", "ipaddress": "9.10.11.12", "status": "RESTART", "anotherkey": "anothervalue" } ]
需要将数组内所有对象合并,输出格式如下(name字段允许是单个值或数组):
[ { "name": "hosts", "ipaddress": ["1.2.3.4", "5.6.7.8", "9.10.11.12"], "status": ["UP", "DOWN", "RESTART"], "randomkey": ["randomvalue"], "newkey": ["newvalue"], "anotherkey": ["anothervalue"] } ]
目前能通过硬编码键的方式实现,比如:
{ ipaddress: (map(.ipaddress) | unique ) } + { status: (map(.status) | unique ) } + { randomkey: (map(.randomkey) | unique ) }
但键是随机生成的,无法预先硬编码,需要一种动态处理所有键的方法。
解决方案:用jq动态遍历合并
可以借助jq的reduce和keys系列函数,无需指定具体键就能完成合并:
核心命令
[reduce .[] as $item ({}; reduce ($item | keys_unsorted[]) as $key (.; .[$key] += [$item[$key]] | .[$key] |= unique ) )]
命令拆解
- 外层
reduce:把数组里的每个对象逐个取出处理,初始时准备一个空对象作为合并结果的容器。 - 内层
reduce:遍历当前对象的所有键(keys_unsorted会保留键的原始顺序,不需要顺序的话用keys就行),不管是固定键还是随机生成的键都能覆盖到。 - 值合并逻辑:
.[$key] += [$item[$key]]:把当前对象中该键对应的值,添加到容器里该键的数组中;如果容器里还没有这个键,会自动创建一个新数组来存值。| .[$key] |= unique:给数组去重,如果你的场景不需要去重,直接删掉这部分即可。
- 外层数组包裹:最后用
[]把合并好的对象包起来,匹配需求的输出格式。
可选:单独处理name字段
如果所有对象的name值都相同,想要让它保持单个值而非数组,可以在合并后追加处理:
[reduce .[] as $item ({}; reduce ($item | keys_unsorted[]) as $key (.; .[$key] += [$item[$key]] | .[$key] |= unique ) ) | .name = .name[0]]
执行结果
运行核心命令后,输出如下:
[ { "name": ["hosts"], "ipaddress": ["1.2.3.4", "5.6.7.8", "9.10.11.12"], "status": ["UP", "DOWN", "RESTART"], "randomkey": ["randomvalue"], "newkey": ["newvalue"], "anotherkey": ["anothervalue"] } ]
如果使用了name字段的特殊处理命令,name会变成单个值"hosts",完全符合需求格式。
内容的提问来源于stack exchange,提问作者dc3
相关产品推荐
相关产品推荐

