给定格式为的JSON文件:
{
"key00" : {
"key10" : {
"20170405" : {
"val0" : 10,
...
"valn" : 12
},
"20170404" : {
"val0" : 5,
...
"valn" : 43
},
...
},
"key11" : {...},
...
},
"key01" : {...},
"key02" : {...},
...
}
我想使用jq
将树分解为扁平列表,其格式如下所示。此过程应选择层次结构中的一个特定键,日期以及树中该日期的每个实例合并该日期的值,同时根据值的位置使其键具有唯一性在树上:
[
{
"date" : "20170405",
"key00.key10.val0" : 10,
...
"key00.key10.valn" : 12
},
{
"date" : "20170404",
"key00.key10.val0" : 10,
...
"key00.key10.valn" : 12
},
...
{
"date" : "20170403",
"key0n.key1n.val0" : 10,
...
"key0n.key1n.valn" : 12
},
]
知道嵌套结构,假设它是刚性的,我用Perl中的一组for循环执行了这个。但如果结构发生变化,程序就会中断。此外,对于每个层次结构,我都需要一个for循环。您将如何使用jq的语言递归遍历此树?
(我想使用jq,因为我已经在使用它将第一个代码清单中的格式合并到一起,所以我认为我可以构建它。合并很简单:jq -s 'reduce .[] as $x ({}, . * $x)' *.json > merged.json
)< / p>
答案 0 :(得分:1)
这可能看起来像是一个艰难的过程,但为了使事情更容易理解,让我们从一个辅助函数开始,当给定一个字符串数组和一个值时,它本质上会创建一个JSON对象,通过创建一个使用连接字符从数组中键:
# input: [arrayOfStrings, value]
def squish(joinchar): { (.[0] | join(joinchar)): .[1] };
例如,[["a","b"], 10] | squish(".")
会发出{"a.b", 10}
问题解决方案的其余部分基于内置过滤器paths
和group_by
,这些过滤器在其他地方有记录,但简而言之,paths
会发出字符串流表示路径的数组;然后加上相关的值。然后使用group_by
按日期对[path,value]数组进行分组。最后,结果根据要求进行格式化。
. as $in
| [paths
| select(length==4)
| . as $path
| [ $path, ($in|getpath($path)) ] ]
| group_by( .[0][2] | tonumber ) # sort by numeric value
| map( {date: .[0][0][2] }
+ ( map( del(.[0][2]) | squish(".") ) | add) )
上述解决方案按日期对全局路径进行分组,这似乎符合除样本输出数据之外的要求。
如果数据与给定的样本不同,则可能必须修改上面使用的select(length==4)
标准。
答案 1 :(得分:0)
以下是使用 to_entries 获取数据的解决方案,将其放入 setpath 接受的表单, group_by 按日期和使用 setpath 减少以构建最终表单。
您可以逐步了解其工作原理。首先从
开始 to_entries
| .[]
| .key as $k1
| ( .value | to_entries
| $k1, .[] )
转到第一把钥匙。用我的测试数据给我
"key00"
{
"key": "key10",
"value": {
"20170405": {
"val0": 10,
"valn": 12
},
"20170404": {
"val0": 5,
"valn": 43
}
}
}
"key01"
...
然后向下钻取一点以获得下一个键
to_entries
| .[]
| .key as $k1
| ( .value | to_entries
| .[]
| .key as $k2
| ( .value | to_entries
| $k1, $k2, .[] ) )
给出了
"key00"
"key10"
{
"key": "20170405",
"value": {
"val0": 10,
"valn": 12
}
}
{
"key": "20170404",
"value": {
"val0": 5,
"valn": 43
}
}
"key01"
"key11"
...
然后多一点来得到日期和最终值
to_entries
| .[]
| .key as $k1
| ( .value | to_entries
| .[]
| .key as $k2
| ( .value | to_entries
| .[]
| .key as $d
| ( .value | to_entries
| .[]
| [$d, [$k1, $k2, .key], .value] ) ) )
现在我们已经
了[
"20170405",
[
"key00",
"key10",
"val0"
],
10
]
[
"20170405",
[
"key00",
"key10",
"valn"
],
12
]
...
将其放回数组并使用 group_by , reduce 和 setpath
[
to_entries
| .[]
| .key as $k1
| ( .value | to_entries
| .[]
| .key as $k2
| ( .value | to_entries
| .[]
| .key as $d
| ( .value | to_entries
| .[]
| [$d, [$k1, $k2, .key], .value]
)
)
)
]
| group_by(.[0])
| .[]
| .[0][0] as $d
| reduce .[] as $e (
{date:$d}
; setpath([$e[1] | join(".")]; $e[2])
)
得到最终答案
{
"date": "20170404",
"key00.key10.val0": 5,
"key00.key10.valn": 43
}
{
"date": "20170405",
"key01.key11.val1": 1,
"key00.key10.valn": 12,
"key00.key10.val0": 10,
"key01.key11.val2": 2
}
{
"date": "20170406",
"key01.key11.val0": 0,
"key01.key11.val9": 9
}
...