json group_by的可重用函数,但返回以group为键的对象

amrnrhlw  于 2023-01-14  发布在  其他
关注(0)|答案(3)|浏览(126)

这个问题是在寻找一个可以导入到其他jq程序中的可重用函数。这个问题group and key by property是在解决一个特定的问题。这个问题的目的是尽可能的一般化,因为这是一个重复出现的问题。
给定以下(示例)输入,如何创建一个新对象,将每个人按其国家分组在一起?我知道group_by,但它返回数组的数组。

[
  {
    "name": "anna",
    "country": "germany"
  },
  {
    "name": "lisa",
    "country": "germany"
  },
  {
    "name": "john",
    "country": "usa"
  }
]

运行group_by(.country)将生成:

[
  [
    {
      "name": "anna",
      "country": "germany"
    },
    {
      "name": "lisa",
      "country": "germany"
    }
  ],
  [
    {
      "name": "john",
      "country": "usa"
    }
  ]
]

但是这种结构使得后续的处理变得困难。相反,我更愿意将文档转换为以下结构:

{
  "germany": [
    {
      "name": "anna",
      "country": "germany"
    },
    {
      "name": "lisa",
      "country": "germany"
    }
  ],
  "usa": [
    {
      "name": "john",
      "country": "usa"
    }
  ]
}

这将使统计每个国家的人口等其他任务容易得多。
我该怎么做呢?如果可能的话,答案不应该依赖于样本格式的确切格式,而应该适用于任意输入的一般情况。

inn6fuwd

inn6fuwd1#

下面是使用reduce而不是group_by的变体:

reduce .[] as $m ({}; .[$m.country] += [$m])

Demo
或作为定义函数:

def grp(f): reduce .[] as $m ({}; .[$m|f] += [$m]);

grp(.country)

Demo

{
  "germany": [
    {
      "name": "anna",
      "country": "germany"
    },
    {
      "name": "lisa",
      "country": "germany"
    }
  ],
  "usa": [
    {
      "name": "john",
      "country": "usa"
    }
  ]
}
i7uq4tfw

i7uq4tfw2#

使用group_bymap()add的较短替代方案:

group_by(.country) | map({ (.[0].country): . }) | add

产生:

{
  "germany": [
    {
      "name": "anna",
      "country": "germany"
    },
    {
      "name": "lisa",
      "country": "germany"
    }
  ],
  "usa": [
    {
      "name": "john",
      "country": "usa"
    }
  ]
}

Jq{Play

wqlqzqxt

wqlqzqxt3#

可以定义一个可重用的函数,通过一个条件对数组进行分组,并使用这个条件作为键。显然,这只适用于字符串键(但总是可以添加|tostring)。

def group(k):
  group_by(k) | map({ key: first|k, value: . }) | from_entries;

转换到预期输出的过程很简单:

group(.country)

然后,统计每个国家的人数等额外任务变得微不足道:

group(.country) | map_values(length)

产生:

{
  "germany": 2,
  "usa": 1
}

链接其他的转换也是直接的,需要每个国家的名字列表吗?

group(.country) | map_values(map(.name))

{
  "germany": [
    "anna",
    "lisa"
  ],
  "usa": [
    "john"
  ]
}

一次性Map这些值可能会有好处。这可以通过第二个参数来实现。现在可以重新定义原始函数以委托给更通用的函数:

def group(k;v):
  group_by(k) | map({ key: first|k, value: map(v) }) | from_entries;
def group(k): group(k; .);
group(.country; .name)

为了允许使用任意对象对对象进行分组,该函数需要第二个参数,然后该参数将把分组转换为字符串键。

def group(group;key):
  group_by(group) | map({key:first|group|key, value:.}) | from_entries;
def group(group): group(group; .);

需要特别注意的是,组的字符串表示必须与组具有1:1Map,否则在from_entries步骤中将丢失一些项。

相关问题