【问题标题】:Merge two array of objects with common key using jq command使用 jq 命令合并两个具有公共键的对象数组
【发布时间】:2023-02-09 04:12:29
【问题描述】:

我有两个数据集:

data1='[
    { "bookings": 2984, "timestamp": 1675854900 },
    { "bookings": 2967, "timestamp": 1675855200 }
]'

data2='[
    { "errors": 51, "timestamp": 1675854900 },
    { "errors": 90, "timestamp": 1675855200 }
]'

我希望输出是:

combined='[
    { "errors": 51, bookings: 2984, "timestamp": 1675854900 },
    { "errors": 90, bookings: 2967, "timestamp": 1675855200 }
]'

这可以通过 shell 脚本和 jq 命令来实现吗?

假设时间戳将始终存在并且在两个数据集中始终具有共同的值。连顺序都是一样的。

【问题讨论】:

  • 一个使用订单保证来优化性能的答案,或者只是一个 FYI 而一个不依赖它的答案也可以接受吗?
  • 另外,是否保证相同的时间戳永远不会在 data1 中出现两次/在 data2 中出现两次?

标签: bash jq


【解决方案1】:

一个简单的JOIN operation 可以做到:

jq -n --argjson data1 "$data1" --argjson data2 "$data2" '
  [JOIN(INDEX($data1[]; .timestamp); $data2[]; .timestamp | @text; add)]
'
[
  {
    "errors": 51,
    "timestamp": 1675854900,
    "bookings": 2984
  },
  {
    "errors": 90,
    "timestamp": 1675855200,
    "bookings": 2967
  }
]

我收到此错误:jq: error: JOIN/4 is not defined at <top-level>, line 2: [JOIN(INDEX($data1[]; .timestamp); $data2[]; .timestamp | @text; add)] jq: 1 compile error

您可能正在使用旧版本的 jq。 JOININDEX是jq 1.6引入的。通过从source 获取它们的定义来自己定义它们,或者获取这些定义并修改它们以适合您的用例(两者都适用于 jq 1.5)。

来源定义:

jq -n --argjson data1 "$data1" --argjson data2 "$data2" '
  def INDEX(stream; idx_expr):
    reduce stream as $row ({}; .[$row | idx_expr | tostring] = $row);
  def JOIN($idx; stream; idx_expr; join_expr):
    stream | [., $idx[idx_expr]] | join_expr;

  [JOIN(INDEX($data1[]; .timestamp); $data2[]; .timestamp | @text; add)]
'

适应您的用例:

jq -n --argjson data1 "$data1" --argjson data2 "$data2" '
  ($data1 | with_entries(.key = (.value.timestamp | @text))) as $ix
  | $data2 | map(. + $ix[.timestamp | @text])
'

【讨论】:

  • 好的。我将完成飞行中的答案,但这要清晰得多。
  • 我收到此错误:jq: error: JOIN/4 is not defined at <top-level>, line 2: [JOIN(INDEX($data1[]; .timestamp); $data2[]; .timestamp | @text; add)] jq: 1 compile error
【解决方案2】:

另一种方法是构建一个从时间戳到错误计数的映射,并在其中执行查找。

jq -n '
  input as $data1
| input as $data2
| ($data2
   | map({ "key": (.timestamp | tostring), "value": .errors }) 
   | from_entries
  ) as $errors_by_timestamp
| $data1 | map(.errors = $errors_by_timestamp[(.timestamp | tostring)])
' <<<"$data1 $data2"

【讨论】:

  • 这个答案对我有用
【解决方案3】:

顺便说一句,我从早上开始就尝试从 AI 那里得到这个答案,这次它终于也给了我正确的解决方案

#!/bin/bash

data1='[
    { "bookings": 2984, "timestamp": 1675854900 },
    { "bookings": 2967, "timestamp": 1675855200 }
]'

data2='[
    { "errors": 51, "timestamp": 1675854900 },
    { "errors": 90, "timestamp": 1675855200 }
]'

combined=$(jq -n --argjson d1 "$data1" --argjson d2 "$data2" '
  [ $d1, $d2 ] | transpose[] | group_by(.timestamp) | map(
    reduce .[] as $i ({}; . * $i)
  )
')

echo "$combined"

把它贴在这里给你们,以防你们没有想到这个方法

【讨论】:

    【解决方案4】:

    一般来说,如果你发现JOIN有点难以理解或使用,那么考虑使用INDEX来解决这类问题。在目前的情况下,您可以使用一种非常简单的方法,例如:

    jq -n --argjson data1 "$data1"  --argjson data2 "$data2" '
      INDEX($data1[]; .timestamp) as $dict
      | $data2 | map( . + $dict[.timestamp|tostring])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-24
      • 1970-01-01
      • 2017-07-14
      • 2023-02-26
      • 1970-01-01
      • 2021-07-21
      相关资源
      最近更新 更多