JQ:计算输入的子集的每组对象数

时间:2018-09-25 11:52:44

标签: json stream grouping jq

我需要用JQ计算每个组中的对象数,但仅适用于N个最新对象。

样本输入,N = 3:

{"modified":"Mon Sep 25 14:20:00 +0000 2018","object_id":1,"group_id":"C"}
{"modified":"Mon Sep 25 14:23:00 +0000 2018","object_id":2,"group_id":"A"}
{"modified":"Mon Sep 25 14:21:00 +0000 2018","object_id":3,"group_id":"B"}
{"modified":"Mon Sep 25 14:22:00 +0000 2018","object_id":4,"group_id":"A"}

预期输出:

{"A",2}
{"B",1}

我什至没有选择一个可以保留对象结构的基于日期的子集:这是我设法实现的最好结果:

 [
   .modified |= strptime("%a %b %d %H:%M:%S %z %Y") |
   .modified |= mktime |
   .modified |= strftime("%Y-%m-%d %H:%M:%S")
 ]  |
 sort_by(.modified) |
 .[] |
 {modified, object_id, group_id}

由于某些原因,结果仍未排序。

我也未能将此类列表转换为仅选择N个最新条目的数组。

然后,我需要以某种方式计算每个组中的对象数。


总的来说,对于对象的数组和列表如何相互转换以及如何修改其某些字段,然后仅提取所需的字段,我似乎需要一个非常直观的解释。不幸的是,到目前为止,我发现的教程没有帮助。

2 个答案:

答案 0 :(得分:3)

假设您的输入文件是:

cat file
{"modified":"Mon Sep 25 14:20:00 +0000 2018","object_id":1,"class_id":"C"}
{"modified":"Mon Sep 25 14:23:00 +0000 2018","object_id":2,"class_id":"A"}
{"modified":"Mon Sep 25 14:21:00 +0000 2018","object_id":3,"class_id":"B"}
{"modified":"Mon Sep 25 14:22:00 +0000 2018","object_id":4,"class_id":"A"}

您可以尝试以下操作:

<file jq -s '
   [ .[] | 
     (.modified |= (strptime("%a %b %d %H:%M:%S +0000 %Y") | mktime)) 
   ] | 
   sort_by(.modified) |              # sort using converted time
   .[-3:] |                          # take the last 3
   group_by(.class_id) |             # group ids together
   .[] |                             
   {(.[0].class_id): length}'        # create the object using the id name and table length
{
   "A": 2
}
{
  "B": 1
}

请注意,在我的系统上,%z中的选项strptime不起作用。因此,我将其替换为+0000(无论如何在时间转换中都没有使用)。

答案 1 :(得分:2)

accepted answer使用-s命令行选项,该选项要求整个输入数据都适合内存。对于非常大的数据集,这可能是不可能的。

自从jq 1.5(在2015年)发布以来,已经有了替代方案。因此,在这里提出了一种使用inputs的内存有效解决方案。

关键功能封装在以下jq过滤器中:

# Return an array of n items as if by 
# [stream] | sort_by(filter) | .[-n:]
def maxn(stream; filter; n):
  def maxn:
    sort_by(filter) | .[-n :];
  reduce stream as $x ([]; . + [$x] | maxn);

现在只需另外三行就可以得到当前问题的解决方案(N == 3):

maxn(inputs; .modified | strptime("%a %b %d %H:%M:%S +0000 %Y") | mktime; 3)
| group_by(.class_id)[]
| {(.[0].class_id): length}

请注意,这假定使用了-n命令行选项。如果省略,则输入的第一行将被忽略。

大N

对于大型数据集,如果N的值也很大,则可能需要对上面的内容进行调整以使用jq的支持二进制搜索(bsearch而不是sort_by的麻烦。类似地,值得缓存mktime值。