使用jq从多个文件聚合json数组,按键分组

使用jq从多个文件聚合json数组,按键分组,json,jq,Json,Jq,我希望将两个或多个文件聚合到一个json中,并在同一个键下聚合数组 file1.json { "shapes": [ { "id": "1", "name": "circle" }, { "id": "2", "name": "square"

我希望将两个或多个文件聚合到一个json中,并在同一个键下聚合数组

file1.json

{
  "shapes": [
    {
      "id": "1",
      "name": "circle"
    },
    {
      "id": "2",
      "name": "square"
    }
  ]
}
file2.json

{
  "shapes": [
    {
      "id": "3",
      "name": "triangle"
    }
  ]
}
预期结果:

{
  "shapes": [
    {
      "id": "1",
      "name": "circle"
    },
    {
      "id": "2",
      "name": "square"
    },
    {
      "id": "3",
      "name": "triangle"
    }
  ]
}
我可以使用以下jq命令执行此操作:

jq -s '{shapes: map(.shapes)|add }' file*.json

但这需要我知道shapes属性并对其进行硬编码。有没有一种简单的方法可以在不显式使用键名的情况下获得相同的结果?

请尝试以下代码。这可以处理任意数量的文件。假设所有输入都是json对象,其中的所有值都是数组。所有这样的数组在按键分组后都被arggregated。它输出一个对象,该对象具有与相应聚合数组关联的键

jq -s 'map(to_entries)|add|group_by(.key)|
    map( { "key": (.[0].key), "value": (map(.value)|add)})|
    from_entries' file1.json file2.json
对于您的示例输入,它给出:

{
  "shapes": [
    {
      "id": "1",
      "name": "circle"
    },
    {
      "id": "2",
      "name": "square"
    },
    {
      "id": "3",
      "name": "triangle"
    }
  ]
}

这里有一个解决方案也解决了一个更一般的问题:首先,它处理任意多个输入文件;第二,假设每个顶级键都是数组值的,它就为每个键按键形成“和”

通用功能:

  # the values at each key are assumed to be arrays
  def aggregate(stream): 
    reduce stream as $o ({}; 
      reduce ($o|keys_unsorted[]) as $k (.; 
        .[$k] += $o[$k] ));
为了避免“咕噜咕噜”,我们将使用
输入

aggregate(inputs)
因此,调用必须使用-n命令行选项:

jq -n -f program.jq *.json

当每个顶级对象只有一个键时,这是一个合适的解决方案,它既高效又概念简单。它假定使用-n选项调用jq

reduce inputs as $in (null;
   ($in|keys_unsorted[0]) as $k | { ($k): (.[$k] + $in[$k]) })
或者更紧凑一点:

reduce inputs as $in (null; ($in|keys_unsorted[0]) as $k | .[$k] += $in[$k] )

如果一个或多个文件中的顶级对象有多个键怎么办?我正在处理一个只有一个键的api响应。因此,它不会影响这种情况,但对于一般情况,所有未命名形状的键都将被有效地丢弃,这可能是我想要的,也可能不是。嗨@peak,我无法重现解决方案。我将函数保存为program.jq并运行
jq-n-f program.jq*.json
,但作为响应,我得到了null。我想我必须用aggregate语句来做一些事情。@moogly81在文件
program.jq
中添加行
aggregate(inputs)
后再试一次,好的,这很有效!聚合(输入)位于程序的末尾。jq文件这非常有效。我不认为这是一个简单的方法,就像在这个问题中所说的那样,但我想这并不容易。不幸的是,这个解决方案是非常低效的(无论是在内存使用和速度方面),还是有问题的(例如,因为它使用的方式<代码>扁平化< /代码>)。我正在改变我对这个问题的接受答案,因为这是我要使用的答案。代码>jq-n“将输入减少为$in(null;($in | keys[0])为$k |[k]+=$in[$k])”文件*.json@peak:未排序的部分是否仅用于性能?在我的具体情况下,我并不在意,除了排序键在可读性上有一点好处;从计算上看,差异可能非常小,甚至可能不存在。