2015-11-06 43 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]) 
)