[英]How to traverse through a tree like nested data structure of unknown depth in order to find and collect addressable array items?
[英]How Traverse nested JSON in order with root node items
我有一个嵌套的 JSON 结构,就像一个播放列表可以有图像、视频一样,这个播放列表也可以有另一个嵌套的播放列表。
所以我希望得到 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}
如果不存在任何type
的file
,这可能会严重崩溃。
这是一种不同的方法:
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,在每次调用时返回下一个的结果。 这四个函数是() => 1
、 foo
、 () => 2
和bar
。 foo
和bar
的结构与foo
使用两个函数() => 3
和() => 4
和bar
使用三个函数() => 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.