如何用jq将对象数组合并为单对象,对应键值转为数组
合并JSON数组为键值数组对象
输入JSON
[{ "first": 12355, "second": "abc" },{ "first": 89010, "second": "def" },{ "first": 23423, "second": "hij" },{ "first": 23456, "second": "klm" },{ "first": 11111, "second": "nop" }]
需求目标
将数组中的所有对象按对应键合并,每个键的值汇总为一个数组,最终得到如下对象:
{ "first" : [12355,89010,23423,23456,11111], "second" : ["abc","def","hij","klm","nop"] }
你的代码问题分析
你当前的reduce代码存在几个逻辑错误:
- 变量命名逻辑颠倒:
reduce .[] as $final里的$final应该指代当前遍历的数组元素,而不是最终结果,这会导致后续赋值逻辑混乱 - 初始空对象的
keys为空:(. | keys) as $k中的.是reduce的初始值{},没有任何键,所以后续的map不会执行任何操作 - 错误使用
map:reduce内部不需要用map来处理,应该直接对每个键进行值的累加操作
正确的jq解决方案
方法一:嵌套reduce(通用适配任意键)
这是最通用的写法,不管输入对象有多少个键,都能自动合并:
reduce .[] as $item ({}; reduce ($item | keys_unsorted)[] as $key (.; .[$key] += [$item[$key]] ) )
逻辑说明:
- 外层
reduce遍历输入数组的每个元素,初始结果为{} - 内层
reduce遍历当前元素的所有键(keys_unsorted保留原键顺序) - 把当前元素的键值追加到结果对象对应键的数组中(如果键不存在,会自动创建数组)
方法二:简洁写法(利用transpose)
如果输入的所有对象键的顺序一致,也可以用更简洁的写法:
[map(to_entries)] | transpose | map({key: .[0].key, value: map(.value)}) | from_entries
逻辑说明:
map(to_entries):把每个对象转成[{key: "...", value: ...}]格式的数组transpose:将数组转置,把相同键的键值对归为一组- 把每组转成
{key: "...", value: [所有对应值]}的格式,最后用from_entries转回对象
运行结果
执行上述任意代码,都会得到你预期的目标JSON。
内容的提问来源于stack exchange,提问作者Christian Bongiorno
相关产品推荐
相关产品推荐

