使用jq递归减少数组

时间:2015-11-06 02:52:39

标签: jq

如何递归查找对象中的所有数组并将它们缩减为第一项?

我尝试使用if .[0]? == "" then .[0] else . end检测数组,但如果当前对象不是数组,则不输出任何内容。

输入:

{
  "a": 1,
  "b": [
    1,
    2,
    3
  ],
  "c": [
    {
      "a": 1,
      "b": [
        1,
        2,
        3
      ],
      "c": {
        "a": 1,
        "b": [
          1,
          2,
          3
        ]
      }
    },
    {
      "a": 1,
      "b": [
        1,
        2,
        3
      ],
      "c": {
        "a": 1,
        "b": [
          1,
          2,
          3
        ]
      }
    },
    {
      "a": 1,
      "b": [
        1,
        2,
        3
      ],
      "c": {
        "a": 1,
        "b": [
          1,
          2,
          3
        ]
      }
    }
  ]
}

输出:

{
  "a": 1,
  "b": [
    1
  ],
  "c": [
    {
      "a": 1,
      "b": [
        1
      ],
      "c": {
        "a": 1,
        "b": [
          1
        ]
      }
    }
  ]
}

3 个答案:

答案 0 :(得分:1)

walk / 1包含在jq的最新版(1.5版)中。它也可以在下面找到。

以下是如何根据我的理解实现您的目标:

walk(if type == "array" and length > 1 then [.[0]] else . end)


# Apply f to composite entities recursively, and to atoms
def walk(f):
  . as $in
  | if type == "object" then
      reduce keys[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

答案 1 :(得分:1)

高峰的答案很棒。使用递归也可以解决这个问题,但是如何使它工作并不明显。

我提出的天真解决方案是(.. | arrays) |= .[0],但这不起作用,因为递归是从外部完成的,这意味着在嵌套的情况下,我们最终试图从值中获取。[0]谁不再是阵列。

这可以通过进行后序遍历来解决,如in this GitHub issue所述。

使用post_recurse,只需在上述天真解决方案中将..替换为post_recurse即可。完整的解决方案:

def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (post_recurse | arrays) |= .[0]

答案 2 :(得分:0)

以下是使用 tostream 转换输入对象的解决方案 进入路径流,过滤掉具有非零数组索引的任何路径 并使用 reduce setpath 将结果转换回对象。所有递归都是 tostream 的内部。

[
  tostream

| if   length != 2 then empty
  elif ([.[0][]|numbers|.!=0]|any) then empty
  else .
  end
]

| reduce .[] as $p (
    {};     
    setpath($p[0]; $p[1])
  )