【问题标题】:recursive reduce arrays using jq使用 jq 递归减少数组
【发布时间】:2015-11-06 02:52:38
【问题描述】:

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

我尝试使用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
        ]
      }
    }
  ]
}

【问题讨论】:

    标签: jq


    【解决方案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;
    

    【讨论】:

      【解决方案2】:

      peak 的回答很棒。这个问题也可以用递归来解决,但是如何让它工作还不是很清楚。

      我想出的天真的解决方案是 (.. | 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]
      

      【讨论】:

        【解决方案3】:

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

        [
          tostream
        
        | if   length != 2 then empty
          elif ([.[0][]|numbers|.!=0]|any) then empty
          else .
          end
        ]
        
        | reduce .[] as $p (
            {};     
            setpath($p[0]; $p[1])
          )
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-05-06
          • 2018-02-09
          • 2017-04-09
          • 2019-06-09
          • 2013-06-13
          • 2011-06-07
          • 2021-06-11
          相关资源
          最近更新 更多