首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用jq将数组分割成块?

如何使用jq将数组分割成块?
EN

Stack Overflow用户
提问于 2018-07-19 00:33:36
回答 5查看 10.1K关注 0票数 20

我有一个包含数组的非常大的JSON文件。是否可以使用jq将该数组拆分为几个固定大小的较小数组?假设我的输入是这样的:[1,2,3,4,5,6,7,8,9,10],我想把它分割成3个元素长的块。jq的期望输出是:

代码语言:javascript
复制
[1,2,3]
[4,5,6]
[7,8,9]
[10]

实际上,我的输入数组有近三百万个元素,都是UUID。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2018-07-19 16:08:09

由于Cédric Connes (github:connesc),以下面向流的window/3定义概括了_nwise,并说明了绕过使用流结束标记的“装箱技术”,因此,如果流包含非JSON值nan,则可以使用。还包括了_nwise/1window/3定义。

window/3的第一个参数被解释为流。$size是窗口大小,$step指定要跳过的值的数量。例如,

代码语言:javascript
复制
window(1,2,3; 2; 1)

产量:

代码语言:javascript
复制
[1,2]
[2,3]

窗口/3和_nsize/1

代码语言:javascript
复制
def window(values; $size; $step):
  def checkparam(name; value): if (value | isnormal) and value > 0 and (value | floor) == value then . else error("window \(name) must be a positive integer") end;
  checkparam("size"; $size)
| checkparam("step"; $step)
  # We need to detect the end of the loop in order to produce the terminal partial group (if any).
  # For that purpose, we introduce an artificial null sentinel, and wrap the input values into singleton arrays in order to distinguish them.
| foreach ((values | [.]), null) as $item (
    {index: -1, items: [], ready: false};
    (.index + 1) as $index
    # Extract items that must be reused from the previous iteration
    | if (.ready | not) then .items
      elif $step >= $size or $item == null then []
      else .items[-($size - $step):]
      end
    # Append the current item unless it must be skipped
    | if ($index % $step) < $size then . + $item
      else .
      end
    | {$index, items: ., ready: (length == $size or ($item == null and length > 0))};
    if .ready then .items else empty end
  );

def _nwise($n): window(.[]; $n; $n);

来源:

https://gist.github.com/connesc/d6b87cbacae13d4fd58763724049da58

票数 4
EN

Stack Overflow用户

发布于 2018-07-19 02:49:12

有一个(无文档化的)内置组件,_nwise,它满足功能需求:

代码语言:javascript
复制
$ jq -nc '[1,2,3,4,5,6,7,8,9,10] | _nwise(3)'

[1,2,3]
[4,5,6]
[7,8,9]
[10]

另外:

代码语言:javascript
复制
$ jq -nc '_nwise([1,2,3,4,5,6,7,8,9,10];3)' 
[1,2,3]
[4,5,6]
[7,8,9]
[10]

顺便说一句,_nwise可以同时用于数组和字符串。

(我认为这是没有文件记载的,因为对一个合适的名字有一些疑问。)

TCO-版本

不幸的是,内置版本是不小心定义的,对于大型数组来说性能不佳。下面是一个优化版本(它应该与非递归版本一样高效):

代码语言:javascript
复制
def nwise($n):
 def _nwise:
   if length <= $n then . else .[0:$n] , (.[$n:]|_nwise) end;
 _nwise;

对于一个3百万大小的数组,这是相当好的表现:在老Mac上3.91s,最大驻留大小为162746368。

请注意,此版本(使用尾调用优化递归)实际上比使用本页其他部分显示的nwise/2的版本快。

票数 22
EN

Stack Overflow用户

发布于 2018-07-19 03:19:46

如果数组太大,无法很好地容纳内存,那么我将采用@CharlesDuffy建议的策略--也就是说,使用面向流的nwise版本将数组元素流转换为jq的第二次调用,例如:

代码语言:javascript
复制
def nwise(stream; $n):
  foreach (stream, nan) as $x ([];
    if length == $n then [$x] else . + [$x] end;
    if (.[-1] | isnan) and length>1 then .[:-1]
    elif length == $n then .
    else empty
    end);

上述各项的“司机”如下:

代码语言:javascript
复制
nwise(inputs; 3)

但是请记住使用-n命令行选项。

若要从任意数组创建流:

代码语言:javascript
复制
$ jq -cn --stream '
    fromstream( inputs | (.[0] |= .[1:])
                | select(. != [[]]) )' huge.json 

因此,shell管道可能如下所示:

代码语言:javascript
复制
$ jq -cn --stream '
    fromstream( inputs | (.[0] |= .[1:])
                | select(. != [[]]) )' huge.json |
  jq -n -f nwise.jq

这种方法很有表现力。若要使用nwise/2将300万项的流分组为3组,

代码语言:javascript
复制
/usr/bin/time -lp

对于jq的第二次调用提供:

代码语言:javascript
复制
user         5.63
sys          0.04
   1261568  maximum resident set size

警告:这个定义使用nan作为流结束标记.因为nan不是JSON值,所以这对处理JSON流来说不会是个问题。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51412721

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档