Rog*_*mbe 8 grouping json jq jsonlines
我有一个可以在每行输出JSON记录的工具,我想使用进行处理jq。
输出看起来像这样:
{"ts":"2017-08-15T21:20:47.029Z","id":"123","elapsed_ms":10}
{"ts":"2017-08-15T21:20:47.044Z","id":"456","elapsed_ms":13}
Run Code Online (Sandbox Code Playgroud)
当我将其传递给jq以下内容时:
./tool | jq 'group_by(.id)'
Run Code Online (Sandbox Code Playgroud)
...它输出一个错误:
jq: error (at <stdin>:1): Cannot index string with string "id"
Run Code Online (Sandbox Code Playgroud)
如何jq处理每行JSON记录数据?
Rog*_*mbe 10
使用--slurp(或-s)开关:
./tool | jq --slurp 'group_by(.id)'
Run Code Online (Sandbox Code Playgroud)
它输出以下内容:
[
[
{
"ts": "2017-08-15T21:20:47.029Z",
"id": "123",
"elapsed_ms": 10
}
],
[
{
"ts": "2017-08-15T21:20:47.044Z",
"id": "456",
"elapsed_ms": 13
}
]
]
Run Code Online (Sandbox Code Playgroud)
...然后可以进一步处理。例如:
./tool | jq -s 'group_by(.id) | map({id: .[0].id, count: length})'
Run Code Online (Sandbox Code Playgroud)
正如 @JeffMercado 指出的, jq 可以很好地处理 JSON 流,但如果您使用group_by,那么您必须确保其输入是一个数组。-s在这种情况下可以使用命令行选项来完成;如果您的 jq 有inputs过滤器,那么也可以将该过滤器与该选项结合使用来完成-n。
但是,如果您有一个 jq 版本inputs(在 jq 1.5 中可用),那么更好的方法是使用以下流式变体group_by:
# sort-free stream-oriented variant of group_by/1
# f should always evaluate to a string.
# Output: a stream of arrays, one array per group
def GROUPS_BY(stream; f): reduce stream as $x ({}; .[$x|f] += [$x] ) | .[] ;
Run Code Online (Sandbox Code Playgroud)
使用示例:GROUPS_BY(inputs; .id)
请注意,您需要将其与-n命令行选项一起使用。
这种流式传输变体有两个主要优点:
group_by/1.请注意,上面的定义GROUPS_BY/2遵循此类流过滤器的约定,因为它生成流。其他变型当然是可能的。
下面说明如何节省内存。假设任务是生成 .id 值的频率计数。单调的解决方案是:
GROUPS_BY(inputs; .id) | [(.[0]|.id), length]
Run Code Online (Sandbox Code Playgroud)
更经济且实际上更好的解决方案是:
GROUPS_BY(inputs|.id; .) | [.[0], length]
Run Code Online (Sandbox Code Playgroud)