如何使用jq将数组拆分为块?

时间:2018-07-19 00:33:36

标签: arrays json shell jq memory-efficient

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

{{1}}

实际上,我的输入数组有将近300万个元素,所有的UUID。

4 个答案:

答案 0 :(得分:8)

有一个_nwise内置(未记录),可以满足功能要求:

$ jq -nc '[1,2,3,4,5,6,7,8,9,10] | _nwise(3)'

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

也:

$ 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版本

不幸的是,内置版本定义不当,对于大型阵列而言效果不佳。这是一个优化的版本(应该与非递归版本一样有效):

def nwise($n):
 def _nwise:
   if length <= $n then . else .[0:$n] , (.[$n:]|_nwise) end;
 _nwise;

对于大小为300万的阵列,这是相当不错的: 在旧Mac上为3.91秒,最大居民容量为162746368。

请注意,此版本(使用尾部调用优化的递归)实际上比使用本页上其他位置的nwise/2的{​​{1}}的版本快。

答案 1 :(得分:2)

由于塞德瑞克·康内斯(CédricConnes),以下window/3面向流的定义 (github:connesc),概括了_nwise, 并说明了 规避了使用拳击技巧的“拳击技术” 流结束标记,因此可以使用 流是否包含非JSON值nan。定义 还包括_nwise/1window/3的值。

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

window(1,2,3; 2; 1)

产量:

[1,2]
[2,3]

窗口/ 3和_nsize / 1

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

答案 2 :(得分:1)

可以肯定的是,以下是骇客-但内存效率高的骇客,即使列表很长:

jq -c --stream 'select(length==2)|.[1]' <huge.json \
| jq -nc 'foreach inputs as $i (null; null; [$i,try input,try input])'

输入JSON文件中的第一条管道流,假设数组由原子值组成(其中[]和{}作为原子值包含在内),每个元素发射一行。因为它以流模式运行,所以即使是单个文档,也不需要将全部内容存储在内存中。

管道的第二部分重复读取多达三项并将它们组合成一个列表。

这应该避免一次在内存中需要三个以上的数据。

答案 3 :(得分:1)

如果数组太大而无法舒适地容纳在内存中,那么我将采用@CharlesDuffy建议的策略-也就是说,使用面向流的{{ 1}},例如:

nwise

上述的“驱动程序”为:

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);

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

要从任意数组创建流,请执行以下操作:

nwise(inputs; 3)

所以shell管道可能看起来像这样:

$ jq -cn --stream '
    fromstream( inputs | (.[0] |= .[1:])
                | select(. != [[]]) )' huge.json 

这种方法非常有效。要使用$ jq -cn --stream ' fromstream( inputs | (.[0] |= .[1:]) | select(. != [[]]) )' huge.json | jq -n -f nwise.jq 将3百万个项目流分为3个组,

nwise/2

第二次调用jq给出:

/usr/bin/time -lp

注意:此定义使用user 5.63 sys 0.04 1261568 maximum resident set size 作为流结束标记。由于nan不是JSON值,因此处理JSON流不会有问题。