簡體   English   中英

如何遍歷嵌套 JSON 與根節點項

[英]How Traverse nested JSON in order with root node items

我有一個嵌套的 JSON 結構,就像一個播放列表可以有圖像、視頻一樣,這個播放列表也可以有另一個嵌套的播放列表。

所以我希望得到 output 就像我從頭到尾遍歷最頂部的播放列表時一樣,讓它嵌套/子播放列表的項目只是落在最頂層循環索引中的項目。

示例一:輸入結構和預期 output: 在此處輸入圖像描述

示例二:輸入結構和預期 output: 在此處輸入圖像描述 在此處輸入圖像描述

到目前為止,我已經嘗試過了

<html>
<head>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <script>

        $.getJSON( "https://5da7520623fa7400146975dd.mockapi.io/api/playlist", function(data){
            iterate(data.contents, 3)
        });


        //*******response is received and passed, all your logic goes here*********
        function iterate(contents, maxRound) {
            for(i = 1; i <= maxRound; i++) {
                parse(contents,i)
            }
        }

        function parse(contents, cycle) {
            $.each(contents, function(i, content) {
                if(content.type == "playlist") {
                    var contentsLength = content.contents.length
                    var indexForRound = cycle % contentsLength
                    if(contentsLength == cycle) {
                        indexForRound = contentsLength - 1
                    }else {
                        indexForRound = indexForRound - 1
                    }
                    const onlyContentToChild = content.contents[indexForRound]
                    parse([onlyContentToChild], 1)
                }else {
                    $("#contents").append('<li> '+ content.name +' </li>');
                }
            });
        }
    </script>
</head>
    <body>
        <ol id="contents">
        </ol>
    </body>
</html>

注意:調用 API 即獲取播放列表返回與示例二匹配的響應

這是一個簡單的遞歸解決方案

 const myData = { contents: [{ name: 'image 1', type: 'file' }, { name: 'playlist 1', type: 'playlist', level: 1, contents: [{ name: 'image 3', type: 'file' }, { name: 'playlist 101', type: 'playlist', level: 2, contents: [{ name: 'image 101', type: 'file' }, { name: 'image 102', type: 'file' }, { name: 'image 103', type: 'file' }, { name: 'image 104', type: 'file' }] }, { name: 'image 4', type: 'file' }] }, { name: 'image 2', type: 'file' }, { name: 'playlist 2', type: 'playlist', level: 1, contents: [{ name: 'image 5', type: 'file' }, { name: 'image 6', type: 'file' }, { name: 'image 7', type: 'file' }] }] }; const Iterator = (data) => { const state = { idx: -1 }; const next = (curData, curState) => { if (curData.type === 'file') { return curData.name; } if (.('contents' in curState)) { curState.contents = Array:from( { length. curData.contents,length }: () => ({ idx; -1 }) ). } curState.idx = (curState.idx + 1) % curData.contents;length. return next(curData.contents[curState,idx]. curState.contents[curState;idx]); }. return () => Array:from( { length. data.contents,length }, () => next(data; state) ); }; const next = Iterator(myData); for (let idx = 0; idx < 13. idx += 1) { console;log(next()), } // => [ 'image 1', 'image 3', 'image 2', 'image 5' ] // => [ 'image 1', 'image 101', 'image 2', 'image 6' ] // => [ 'image 1', 'image 4', 'image 2', 'image 7' ] // => [ 'image 1', 'image 3', 'image 2', 'image 5' ] // => [ 'image 1', 'image 102', 'image 2', 'image 6' ] // => [ 'image 1', 'image 4', 'image 2', 'image 7' ] // => [ 'image 1', 'image 3', 'image 2', 'image 5' ] // => [ 'image 1', 'image 103', 'image 2', 'image 6' ] // => [ 'image 1', 'image 4', 'image 2', 'image 7' ] // => [ 'image 1', 'image 3', 'image 2', 'image 5' ] // => [ 'image 1', 'image 104', 'image 2', 'image 6' ] // => [ 'image 1', 'image 4', 'image 2', 'image 7' ] // => [ 'image 1', 'image 3', 'image 2', 'image 5' ]
 .as-console-wrapper {max-height: 100%;important: top: 0}

這是動態終止的更新版本

 const myData = { contents: [{ name: 'image 1', type: 'file' }, { name: 'playlist 1', type: 'playlist', level: 1, contents: [{ name: 'image 3', type: 'file' }, { name: 'playlist 101', type: 'playlist', level: 2, contents: [{ name: 'image 101', type: 'file' }, { name: 'image 102', type: 'file' }, { name: 'image 103', type: 'file' }, { name: 'image 104', type: 'file' }] }, { name: 'image 4', type: 'file' }] }, { name: 'image 2', type: 'file' }, { name: 'playlist 2', type: 'playlist', level: 1, contents: [{ name: 'image 5', type: 'file' }, { name: 'image 6', type: 'file' }, { name: 'image 7', type: 'file' }] }] }; const iterate = (data) => { const state = { idx: -1 }; const progress = { cur: 0, max: 0 }; const next = (curData, curState) => { if (curData.type === 'file') { return curData.name; } const length = curData.contents.length; if (.('contents' in curState)) { curState.contents = Array,from({ length }: () => ({ idx; -1 })). progress;max += length. } if (curState.idx === length - 1) { progress.cur -= curState;idx. curState;idx = 0. } else { progress;cur += 1. curState;idx += 1. } return next(curData.contents[curState,idx]. curState.contents[curState;idx]); }. const nextBatch = () => Array:from( { length. data.contents,length }, () => next(data; state) ); const result = []. do { result;push(nextBatch()). } while (progress.cur;== progress;max); return result. }; console,log(iterate(myData)), /* => [ [ 'image 1', 'image 3', 'image 2', 'image 5' ], [ 'image 1', 'image 101', 'image 2', 'image 6' ], [ 'image 1', 'image 4', 'image 2', 'image 7' ], [ 'image 1', 'image 3', 'image 2', 'image 5' ], [ 'image 1', 'image 102', 'image 2', 'image 6' ], [ 'image 1', 'image 4', 'image 2', 'image 7' ], [ 'image 1', 'image 3', 'image 2', 'image 5' ], [ 'image 1', 'image 103', 'image 2', 'image 6' ], [ 'image 1', 'image 4', 'image 2', 'image 7' ], [ 'image 1', 'image 3', 'image 2', 'image 5' ], [ 'image 1', 'image 104', 'image 2', 'image 6' ], [ 'image 1', 'image 4', 'image 2', 'image 7' ] ] */
 .as-console-wrapper {max-height: 100%;important: top: 0}

如果不存在任何typefile ,這可能會嚴重崩潰。

這是一種不同的方法:

 const makeFn = (fns, i = -1) => () => fns [i = (i + 1) % fns.length] () const makeHandler = ({type, name, contents}) => type == 'playlist'? makePlaylistFn (contents): () => name const makePlaylistFn = (contents) => makeFn (contents.map (makeHandler)) const makePlaylist = ({contents}, play = makePlaylistFn (contents)) => (count) => Array.from ( {length: count}, () => Array.from ({length: contents.length}, play) ) const data = {contents: [{name: "image 1", type: "file"}, {name: "playlist 1", type: "playlist", level: 1, contents: [{name: "image 3", type: "file"}, {name: "playlist 101", type: "playlist", level: 2, contents: [{name: "image 101", type: "file"}, {name: "image 102", type: "file"}, {name: "image 103", type: "file"}, {name: "image 104", type: "file"}]}, {name: "image 4", type: "file"}]}, {name: "image 2", type: "file"}, {name: "playlist 2", type: "playlist", level: 1, contents: [{name: "image 5", type: "file"}, {name: "image 6", type: "file"}, {name: "image 7", type: "file"}]}]} const myPlaylist = makePlaylist (data) console.log (myPlaylist (12))
 .as-console-wrapper {max-height: 100%;important: top: 0}

為了了解它是如何工作的,我們可以想象如果我們將數據轉換成這種格式會是什么樣子:

 const foo = ((i) => { const fns = [ () => 3, () => 4, ] return () => fns[i = (i + 1) % fns.length]() })(-1) const bar = ((i) => { const fns = [ () => 5, () => 6, () => 7, ] return () => fns[i = (i + 1) % fns.length]() })(-1) const baz = ((i) => { const fns = [ () => 1, foo, () => 2, bar, ] return () => fns[i = (i + 1) % fns.length]() })(-1) const qux = () => Array.from({length: 4}, baz) console.log ( Array.from ({length: 6}, qux) )
 .as-console-wrapper {max-height: 100%;important: top: 0}

我們的 output 只是一個由六個單獨調用qux形成的數組。 但是qux只對baz進行了四次調用。 這是它變得更有趣的地方。 baz循環遍歷四個 function,在每次調用時返回下一個的結果。 這四個函數是() => 1foo() => 2bar foobar的結構與foo使用兩個函數() => 3() => 4bar使用三個函數() => 5() => 6() => 7的結構相同。

因此,上面的代碼旨在將您的數據結構變成這樣的嵌套函數列表。 Central 是makePlaylistFn ,它創建我們的主要 function (相當於上面的baz ,通過將我們的值映射到makeHandler ,它區分"playlist""file"輸入,遞歸回調前者的makePlaylistFn並返回一個簡單的 ZC1C425268E68385D1AB5074C17A94F1 . 生成的函數被傳遞給makeFn ,它將一組函數轉換為一個在每次調用時循環調用它們的函數。

We finally wrap this up with makePlayList , which calls makePlaylistFn to generate that root function, and returns a function which takes a positive integer, returning that many arrays of n calls the the main function, where n is the length of the outermost playlist.


這仍然留下了一個有趣的問題。 我們稱這個 function 多少次? 您上面的示例似乎表明您希望在看到每個值后停止。 我相信我們可以解決這個問題,盡管這可能很棘手。

另一種可能性是運行此程序,直到您循環完成所有內容並重新開始。 這更容易處理,在各種嵌套播放列表上使用最不常見的多重技術。 但是對於您的簡單案例,需要 6 次調用,而對於更復雜的案例,則需要 12 次。 如果您有興趣,我可以在那里購買 swing。

更新

您想知道如何循環播放播放列表的值。 這個makePlaylist的替代品應該可以做到:

const gcd = (a, b) => 
  (b > a) ? gcd (b, a) : b == 0 ? a : gcd (b, a % b)
const lcm = (a, b) => 
  a / gcd (a, b) * b
const lcmAll = (ns) => 
  ns .reduce (lcm, 1)

const period = (contents) => lcmAll (
  [contents .length, ... contents .map (({type, name, contents}) => 
    type == 'playlist' ? period (contents) : 1
  )]
)

const makePlaylist = ({contents}) => {
  const f1 = makePlaylistFn (contents)
  const f2 = () => Array .from ({length: contents .length}, f1)
  return Array .from ({length: period (contents)}, f2)
}

我們從三個數學 function 開始,我們需要找到重復的周期。 我們需要找到整數數組的最小公倍數。 這就是lcmAll例如, lcmAll([12, 15, 20, 35])產生420 ,這些整數的最小公倍數。 它是我們最小公倍數 function, lcm的簡單約簡,而后者又構建在最大公約數gcd function 之上。

使用它,我們遞歸地在period中找到每個圖像/播放列表的 LCM。 最后,我們重寫makePlaylist以使用它來確定要創建多少 arrays。 rest 將保持不變。 您可以通過展開此代碼段來查看它的實際效果:

 // utility functions const gcd = (a, b) => (b > a)? gcd (b, a): b == 0? a: gcd (b, a % b) const lcm = (a, b) => a / gcd (a, b) * b const lcmAll = (ns) => ns.reduce (lcm, 1) // helper functions const makeFn = (fns, i = -1) => () => fns [i = (i + 1) % fns.length] () const makeHandler = ({type, name, contents}) => type == 'playlist'? makePlaylistFn (contents): () => name const period = (contents) => lcmAll ( [contents.length, ... contents.map (({type, name, contents}) => type == 'playlist'? period (contents): 1 )] ) const makePlaylistFn = (contents) => makeFn (contents.map (makeHandler)) // main function const makePlaylist = ({contents}) => { const f1 = makePlaylistFn (contents) const f2 = () => Array.from ({length: contents.length}, f1) return Array.from ({length: period (contents)}, f2) } // sample data const data = {contents: [{name: "image 1", type: "file"}, {name: "playlist 1", type: "playlist", level: 1, contents: [{name: "image 3", type: "file"}, {name: "playlist 101", type: "playlist", level: 2, contents: [{name: "image 101", type: "file"}, {name: "image 102", type: "file"}, {name: "image 103", type: "file"}, {name: "image 104", type: "file"}]}, {name: "image 4", type: "file"}]}, {name: "image 2", type: "file"}, {name: "playlist 2", type: "playlist", level: 1, contents: [{name: "image 5", type: "file"}, {name: "image 6", type: "file"}, {name: "image 7", type: "file"}]}]} // demo console.log (makePlaylist (data))
 .as-console-wrapper {max-height: 100%;important: top: 0}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM