简体   繁体   English

如何对 jq 中的地图数组中的值求和?

[英]How do I sum the values in an array of maps in jq?

Given a JSON stream of the following form:给定以下形式的 JSON 流:

{ "a": 10, "b": 11 } { "a": 20, "b": 21 } { "a": 30, "b": 31 }

I would like to sum the values in each of the objects and output a single object, namely:我想对每个对象中的值求和并输出一个对象,即:

{ "a": 60, "b": 63 }

I'm guessing this will probably require flattening the above list of objects into a an array of [name, value] pairs and then summing the values using reduce but the documentation of the syntax for using reduce is woeful.我猜这可能需要扁平化对象的上述名单成一个数组[name, value]对,然后使用求和值reduce ,但语法的使用文档reduce是前景堪忧。

Unless your jq has inputs , you will have to slurp the objects up using the -s flag.除非您的 jq 有inputs ,否则您将不得不使用-s标志来吸食对象。 Then you'll have to do a fair amount of manipulation:然后你必须做大量的操作:

  1. Each of the objects needs to be mapped out to key/value pairs每个对象都需要映射到键/值对
  2. Flatten the pairs to a single array将这些对展平为单个数组
  3. Group up the pairs by key按密钥对配对进行分组
  4. Map out each group accumulating the values to a single key/value pair映射出每组将值累积到单个键/值对
  5. Map the pairs back to an object将这些对映射回一个对象
map(to_entries)
    | add
    | group_by(.key)
    | map({
          key: .[0].key,
          value: map(.value) | add
      })
    | from_entries

With jq 1.5, this could be greatly improved: You can do away with slurping and just read the inputs directly.使用 jq 1.5,这可以得到很大的改进:您可以取消 slurping 并直接读取inputs

$ jq -n '
reduce (inputs | to_entries[]) as {$key,$value} ({}; .[$key] += $value)
' input.json

Since we're simply accumulating all the values in each of the objects, it'll be easier to just run through the key/value pairs of all the inputs, and add them all up.由于我们只是简单地累加每个对象中的所有值,因此只需遍历所有输入的键/值对并将它们全部加起来会更容易。

I faced the same question when listing all artifacts from GitHub (see here for details) and want to sum their size.我在列出来自 GitHub 的所有工件时遇到了同样的问题(请参阅此处了解详细信息)并想对它们的大小求和。

curl https://api.github.com/repos/:owner/:repo/actions/artifacts \
     -H "Accept: application/vnd.github.v3+json" \
     -H "Authorization:  token <your_pat_here>" \
     | jq '.artifacts | map(.size_in_bytes) | add'

Input:输入:

{
  "total_count": 3,
  "artifacts": [
    {
      "id": 0000001,
      "node_id": "MDg6QXJ0aWZhY3QyNzUxNjI1",
      "name": "artifact-1",
      "size_in_bytes": 1,
      "url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751625",
      "archive_download_url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751625/zip",
      "expired": false,
      "created_at": "2020-03-10T18:21:23Z",
      "updated_at": "2020-03-10T18:21:24Z"
    },
    {
      "id": 0000002,
      "node_id": "MDg6QXJ0aWZhY3QyNzUxNjI0",
      "name": "artifact-2",
      "size_in_bytes": 2,
      "url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751624",
      "archive_download_url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751624/zip",
      "expired": false,
      "created_at": "2020-03-10T18:21:23Z",
      "updated_at": "2020-03-10T18:21:24Z"
    },
    {
      "id": 0000003,
      "node_id": "MDg6QXJ0aWZhY3QyNzI3NTk1",
      "name": "artifact-3",
      "size_in_bytes": 3,
      "url": "https://api.github.com/repos/docker/mercury-ui/actions/artifacts/2727595",
      "archive_download_url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2727595/zip",
      "expired": false,
      "created_at": "2020-03-10T08:46:08Z",
      "updated_at": "2020-03-10T08:46:09Z"
    }
  ]
}

Output:输出:

6

Another approach, which illustrates the power of jq quite nicely, is to use a filter named "sum" defined as follows:另一种很好地说明了 jq 功能的方法是使用一个名为“sum”的过滤器,定义如下:

def sum(f): reduce .[] as $row (0; . + ($row|f) );

To solve the particular problem at hand, one could then use the -s (--slurp) option as mentioned above, together with the expression:为了解决手头的特定问题,可以使用上面提到的-s (--slurp) 选项以及表达式:

{"a": sum(.a), "b": sum(.b) }  # (2)

The expression labeled (2) only computes the two specified sums, but it is easy to generalize, eg as follows:标记为 (2) 的表达式仅计算两个指定的总和,但很容易推广,例如如下:

# Produce an object with the same keys as the first object in the 
# input array, but with values equal to the sum of the corresponding
# values in all the objects.
def sumByKey:
  . as $in
  | reduce (.[0] | keys)[] as $key
    ( {}; . + {($key): ($in | sum(.[$key]))})
;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM