【问题标题】:jq - create new object based on existing objectsjq - 基于现有对象创建新对象
【发布时间】:2021-01-14 18:11:24
【问题描述】:

这是我的第一篇文章,如果不完善请见谅。

我有如下的 json。它由两部分组成。第一部分包含有关用户的信息(id、全名和电子邮件),第二部分包含团队用户所属的信息。 (身份、团队、角色) 我想得到一个对象,它包含:id、fullName、emaila、team 和 role。我可以做到,但前提是用户属于一个团队。如果用户属于多个团队 - 我无法处理。

在我的 json 下面:

[
  {
    "id": "user1",
    "fullName": "User One",
    "email": "user.one@my.mail.com"
  },
  {
    "id": "user2",
    "fullName": "User Two",
    "email": "user.two@my.mail.com"
  },
  {
    "id": "user1",
    "team": "Team_A",
    "role": "TEAM_MEMBER"
  },
  {
    "id": "user1",
    "team": "Team_B",
    "role": "TEAM_ADMIN"
  },
  {
    "id": "user2",
    "team": "Team_B",
    "role": "TEAM_ADMIN"
  }
]

当我使用时:group_by(.id)[] | add 我明白了:

{
  "id": "user1",
  "fullName": "User One",
  "email": "user.one@my.mail.com",
  "team": "Team_B",
  "role": "TEAM_ADMIN"
},
{
  "id": "user2",
  "fullName": "User Two",
  "email": "user.two@my.mail.com",
  "team": "Team_B",
  "role": "TEAM_ADMIN"
}

这几乎是我想要实现的目标。我的目标是:

{
  "id": "user1",
  "fullName": "User One",
  "email": "user.one@my.mail.com",
  "team": "Team_A,
  "role": "TEAM_MEMBER"
},
{
  "id": "user1",
  "fullName": "User One",
  "email": "user.one@my.mail.com",
  "team": "Team_B",
  "role": "TEAM_ADMIN"
},
{
  "id": "user2",
  "fullName": "User Two",
  "email": "user.two@my.mail.com",
  "team": "Team_B",
  "role": "TEAM_ADMIN"
}

我也试过reduce,但没有成功。 我做了很多尝试,但都没有达到预期的效果。

如何使用jq

提前致谢,

krzyhon

【问题讨论】:

  • 您显示的预期输出不是有效的 JSON。请说清楚。你想要一个带有“id”键的对象的 JSON 数组吗?一个流?还有什么?

标签: json join merge jq concat


【解决方案1】:

您似乎收集了一些关于用户的信息。您不能通过合并具有相同 id 的所有内容来将它们压平,它们需要分开。

您有“用户信息”(全名和电子邮件)和“团队信息”。您需要按 id 分组,然后按类型分组,然后分发“用户信息”。

这是您可以采取的一种方法。

# partition the data by "user/team type"
reduce .[] as $i ({}; if "fullName" | in($i) then .user += [$i] else .team += [$i] end)

  # create a lookup of "user" data
  | (.user | INDEX(.id)) as $user

  # group the "team" objects by team
  | .team | group_by(.team)

  # merge corresponding "user info" with all team objects
  | map(map(. + $user[.id]))
[
  [
    {
      "id": "user1",
      "team": "Team_A",
      "role": "TEAM_MEMBER",
      "fullName": "User One",
      "email": "user.one@my.mail.com"
    }
  ],
  [
    {
      "id": "user1",
      "team": "Team_B",
      "role": "TEAM_ADMIN",
      "fullName": "User One",
      "email": "user.one@my.mail.com"
    },
    {
      "id": "user2",
      "team": "Team_B",
      "role": "TEAM_ADMIN",
      "fullName": "User Two",
      "email": "user.two@my.mail.com"
    }
  ]
]

jqplay


这是另一个更简洁的解决方案,假设每个 id 只有一个“用户信息”。

# group by id
group_by(.id) | map(
    # for each group, partition by "type"
    group_by(.fullName)

      # create combinations of all the info and team objects and merge them
      | combinations | add
)

jqplay

【讨论】:

  • 谢谢@Jeff!你解决了我的问题!现在我需要分析一下你是怎么做到的。
【解决方案2】:

此解决方案使用基于combinations 的@JeffMercado 方法的变体,但只使用一次group_by(这意味着对于大型数据集,它应该更有效,因为group_by 是一个相对昂贵的操作)。

这里建议的解决方案会生成一个您需要的 JSON 对象数组,但如果您想要一个 JSON 对象流,只需省略外部方括号即可。

[group_by(.id)[]
 | [map(select(has("team")|not)), map(select(has("team")))]
 | combinations
 | add]

更高效...

为了完全避免使用group_byselect,我们可以使用group_by 的以下泛型变体:

def aggregate_by(s; f):
  reduce s as $x (null; .[$x|f] += [$x]);

解决方案现在可以写成如下:

[ aggregate_by(.[]; .id)[]
  | aggregate_by(.[]; .team == null | tostring)
  | [.[]]
  | combinations
  | add ]

【讨论】:

  • 感谢@peak 的回复。由于我对 jq 还很陌生,请您指出我应该在哪里定义 aggregate_by ()。
  • 最简单的方法是将所有相关代码(按所示顺序)放在一个文件中,并使用 -f 选项调用 jq。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-24
  • 1970-01-01
  • 2019-09-29
  • 1970-01-01
  • 2018-12-13
  • 2015-09-24
  • 2018-10-19
相关资源
最近更新 更多