如何用JSONata优雅实现按时间重组时序数据层级?
时序数据层级翻转的JSONata优化方案
需求说明
需要将按系列分组的时序数据,转换为按时间戳分组的结构:原始数据中每个系列包含独立的data数组(元素含time和value),目标是生成一个对象数组,每个对象以time为标识,同时包含所有系列对应时间点的数值。
原始数据
{ "series1": { "data": [ {"time": "2024-01-01T00:00:00Z", "value": 10}, {"time": "2024-01-02T00:00:00Z", "value": 20} ] }, "series2": { "data": [ {"time": "2024-01-01T00:00:00Z", "value": 100}, {"time": "2024-01-02T00:00:00Z", "value": 200} ] } }
目标输出
[ {"time": "2024-01-01T00:00:00Z", "series1": 10, "series2": 100}, {"time": "2024-01-02T00:00:00Z", "series1": 20, "series2": 200} ]
常见初始实现(参考)
如果你的当前实现是类似硬编码系列名称、手动查找时间匹配值的写法,比如:
$map( $distinct($.series1.data.time & $.series2.data.time), function($t) { { "time": $t, "series1": ($.series1.data[$time = $t].value)[0], "series2": ($.series2.data[$time = $t].value)[0] } } )
这种写法的问题是扩展性差——新增系列时需要修改表达式,且多次查找效率较低。
优化后的JSONata表达式
$map( $groupBy( $flatten($.($map(data, function($d) { $merge([$d, {series: $$}]) }))), "time" ), function($group, $time) { {"time": $time} & $reduce($group, function($acc, $item) { $acc & {$item.series: $item.value} }, {}) } )
优化点说明
- 动态适配任意系列数量:通过
$.($map(data, ...))遍历所有顶层系列,不需要硬编码series1/series2,新增系列时无需修改表达式。 - 高效分组:用
$groupBy直接按time对所有数据项分组,替代手动去重时间+逐个查找的繁琐逻辑。 - 简洁对象合并:使用JSONata的
&操作符简化对象合并,比$merge更直观;$reduce动态构建每个时间点的系列数值字段。
更精简的版本
如果追求极致简洁,还可以写成:
$map($groupBy($flatten($.data~>$map(function($d) { $d & {series: $$} })), "time"), function($g, $t) { {"time": $t} & $g~>$reduce(function($a, $v) { $a & {$v.series: $v.value} }, {}) })
内容的提问来源于stack exchange,提问作者Svet
相关产品推荐
相关产品推荐

