我正在处理一个大型 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()
通过为每个组创建一个对象将这些组聚集在一起。该对象具有item
和attributes
键,并且键的值item
是从组的第一个元素中任意获取的(它们都是相同的)。
进一步group_by()
并map()
为键创建值attributes
。这次对原始对象数组type
中的键进行分组,并且对于每个创建的组,从原始对象中收集和值。attributes
type
colour
您也可以使用 来执行此操作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)
attributes
type
在性能方面,对于小输入,上述两种解决方案彼此相当,而对于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