使用jq处理连续eventId的JSON消息合并(浮点数精度异常)
合并连续eventId的消息字段
需求说明
给定如下JSON结构,当eventId为连续值时合并对应message字段;但若message以# Time:开头,即使eventId连续也需分隔处理。
原始JSON结构
{ "events": [ { "message": "# Time: 1", "eventId": "38636469249093328935961608873790523617989208925384015872" }, { "message": ", 2, 3", "eventId": "38636469249093328935961608873790523617989208925384015873" }, { "message": "# Time: 11", "eventId": "38636469249093328935961608873790523617989208925384015875" }, { "message": "# Time: 12", "eventId": "38636469249093328935961608873790523617989208925384015876" }, { "message": "# Time: A", "eventId": "1" }, { "message": ", B, C", "eventId": "2" }, { "message": "# Time: C", "eventId": "3" }, { "message": "# Time: D", "eventId": "5" } ] }
期望输出
[ { "message": "# Time: 1, 2, 3" }, { "message": "# Time: 11" }, { "message": "# Time: 12" }, { "message": "# Time: A , B, C" }, { "message": "# Time: C" }, { "message": "# Time: D" } ]
尝试过程及问题
1. 初始jq命令(浮点数精度问题)
使用以下命令时,因大eventId转为浮点数丢失精度,导致连续eventId判断失效,未正确合并消息:
jq -r '.events | reduce .[1:][] as $i (.[:1]; if ((.[-1].eventId | tonumber + 1 | tostring) != $i.eventId) or ($i.message | startswith("# Time:")) then . += [$i] else .[-1].message += " " + $i.message end ) | del(.[].eventId) '
执行结果:
[ { "message": "# Time: 1" }, { "message": ", 2, 3" }, { "message": "# Time: 11" }, { "message": "# Time: 12" }, { "message": "# Time: A , B, C" }, { "message": "# Time: C" }, { "message": "# Time: D" } ]
2. 自定义字符串加法函数(逻辑错误)
尝试自定义字符串加法函数处理大数字,但函数逻辑有误,导致小eventId的连续判断也失效:
jq ' # Function to add one to a large number represented as a string def add_one(num): (num | split("") | reverse | map(tonumber)) as $digits | reduce range(0; length) as $i ([]; . + if $i == 0 or .[-1] == 10 then [($digits[$i] + 1) % 10] else [$digits[$i]] end ) | reverse | map(tostring) | join(""); # Process events and check continuity .events | map(.eventId |= tostring) # Ensure all eventIds are strings | reduce .[1:][] as $i (.[:1]; if (add_one(.[-1].eventId) != $i.eventId) or ($i.message | startswith("# Time:")) then . += [$i] else .[-1].message += " " + $i.message end ) | del(.[].eventId) '
执行结果:
[ { "message": "# Time: 1" }, { "message": ", 2, 3" }, { "message": "# Time: 11" }, { "message": "# Time: 12" }, { "message": "# Time: A" }, { "message": ", B, C" }, { "message": "# Time: C" }, { "message": "# Time: D" } ]
当前使用jq 1.7.1版本,需在不使用gojq的前提下解决问题。
解决方案
问题出在两个地方:一是自定义的add_one函数逻辑错误(未正确处理进位),二是判断条件的顺序需要调整(先判断当前message是否以# Time:开头,再判断eventId连续性)。
修正后的jq命令如下:
jq ' # 正确实现字符串数字加1的函数 def add_one(s): s | split("") | reverse | map(tonumber) | reduce .[] as $d ([[], false]; if .[1] then if $d == 9 then [[0] + .[0], true] else [[$d + 1] + .[0], false] end else if $d == 9 then [[0] + .[0], true] else [[$d + 1] + .[0], false] end end ) | if .[1] then [1] + .[0] else .[0] end | map(tostring) | join(""); .events | reduce .[] as $item ([]; if length == 0 then # 初始化第一个元素 [$item] else # 先判断当前消息是否是新的Time条目 if $item.message | startswith("# Time:") then . += [$item] else # 再判断eventId是否连续 if add_one(.[-1].eventId) == $item.eventId then .[-1].message += " " + $item.message else . += [$item] end end end ) | del(.[].eventId) '
说明
- 修正
add_one函数:正确处理数字进位,包括所有位都是9的极端情况(如"999"加1变为"1000")。 - 调整判断顺序:先检查当前message是否以
# Time:开头,若是则直接作为新条目;否则再判断eventId是否连续,连续则合并消息。 - 初始化逻辑优化:从空数组开始处理,避免原逻辑中
.[:1]的局限性。
执行该命令后,输出与期望结果完全一致。
内容的提问来源于stack exchange,提问作者P.Lonnie
相关产品推荐
相关产品推荐

