2015-11-06 116 views
2

如何递归查找对象中的所有数组并将其减少到第一项?使用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 
     ] 
     } 
    } 
    ] 
} 

回答

2

步行/ 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

峰的回答是伟大的。这个问题也可以用递归来解决,但是如何使它工作并不明显。

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

这里使用tostream其中输入对象 转换成的路径的流的溶液中,滤出具有非零阵列索引 任何路径和将结果转换回使用对象减少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]) 
)