jq 嵌套归约/JSON 数据分组

jq 嵌套归约/JSON 数据分组

我正在处理一个大型 JSON 数据集(1GB+),需要合并相似的数组对象,然后合并它们嵌套的相似对象。首先,我有如下行:

[{"item": "item1", "attributes":[{"type": "itemtype", "colour": ["blue"]}]},
{"item": "item1", "attributes":[{"type": "itemtype", "colour": ["grey"]}]},
{"item": "item2", "attributes":[{"type": "itemtype", "colour": ["blue"]}]},
{"item": "item2", "attributes":[{"type": "itemtype2", "colour": ["orange"]}]},
{"item": "item2", "attributes":[{"type": "itemtype2", "colour": ["blue"]}]},
{"item": "item3", "attributes":[{"type": "itemtype", "colour": ["blue"]}]}]

我已经使用 jq 进行分组并用代码漂亮地打印它们:

 jq 'group_by(.item) | map({"item": .[0].item, "attributes": map(.attributes[])})

按项目分组,并对属性进行排序:

[
  {
    "item": "item1",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      },
      {
        "type": "itemtype",
        "colour": [
          "grey"
        ]
      }
    ]
  },
  {
    "item": "item2",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      },
      {
        "type": "itemtype2",
        "colour": [
          "orange"
        ]
      },
      {
        "type": "itemtype2",
        "colour": [
          "blue"
        ]
      }
    ]
  },
  {
    "item": "item3",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      }
    ]
  }
]

我的挑战是将这些嵌套属性分组在一起,按类型分组并根据类型将颜色添加到一个数组中。例如我会有这样的东西:

[
  {
    "item": "item1",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue",
          "grey"
        ]
      }
    ]
  },
  {
    "item": "item2",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      },
      {
        "type": "itemtype2",
        "colour": [
          "orange",
          "blue"
        ]
      }
    ]
  },
  {
    "item": "item3",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      }
    ]
  }
]

我尝试过使用 Lodash 或 JMESPath 的在线编辑器来尝试更好地理解它,并尝试map()在其中添加另一个编辑器map(.attributes[]),但没有任何进展。我想我需要在某个地方添加reduce(),但我无法理解它。

谢谢!

答案1

谢谢你提出这个问题。合作起来很愉快。


这是一个没有任何的变体reduce

group_by(.item) |
map({
    item: first.item,
    attributes: (
        group_by(.attributes[].type) |
        map({
            type: first.attributes[].type,
            colour: ( map(.attributes[].colour[]) )
        })
    )
})

我首先按键对原始元素进行分组item。这给了我们一个按 分组的数组item。该数组中的每个元素都包含一个子数组,其中包含具有相同item.

第一个map()通过为每个组创建一个对象将这些组聚集在一起。该对象具有itemattributes键,并且键的值item是从组的第一个元素中任意获取的(它们都是相同的)。

进一步group_by()map()为键创建值attributes。这次对原始对象数组type中的键进行分组,并且对于每个创建的组,从原始对象中收集和值。attributestypecolour


您也可以使用 来执行此操作reduce,如下所示:

group_by(.item) |
map(reduce .[] as $a ({}; .item = $a.item | .attributes += $a.attributes)) |
map(.attributes |= (
    group_by(.type) |
    map(reduce .[] as $a ({}; .type = $a.type | .colour += $a.colour))
))

这或多或少是group_by()为了创建结果的外部结构,即根据外部结构map(reduce)将数据分组为多个部分并对其进行组织。数组item的值attributes只是被传递。

然后对每个组的数组重复此模式 ( group_by()+ ) ,以便根据值对其进行分组和组织。map(reduce)attributestype


在性能方面,对于小输入,上述两种解决方案彼此相当,而对于reduce小于问题大小(约 64 KB)150 倍的输入,第二种变体(使用 )速度稍快一些。对于这些大小输入,在我的系统上运行大约需要 70 毫秒,即运行时间可以忽略不计。

当输入进一步增长时,reduced基于 的解决方案似乎需要指数级更多的时间。对于大小为 4 MB 的输入,第一个代码使用大约 800 毫秒,而第二个代码单次运行需要 25 秒。

如果jq难以运行更大的输入(线性外推表明运行 1 GB 数据集大约需要 54 小时),您可能需要考虑以某种方式处理数据(可能在数据库中或至少在工作中)数据采用非 JSON 的其他格式)。

例如,给定的数据可以转换为 CSV:

item,type,colour
item1,itemtype,blue
item1,itemtype,grey
item2,itemtype,blue
item2,itemtype2,orange
item2,itemtype2,blue
item3,itemtype,blue

...或进入等效的数据库表,然后从那里开始工作。

以米勒为例,

$ mlr --csv nest --ivar ';' -f colour file.csv
item,type,colour
item1,itemtype,blue;grey
item2,itemtype,blue
item2,itemtype2,orange;blue
item3,itemtype,blue

相关内容