如何解决如何使用根节点项按顺序遍历嵌套的 JSON
我有一个嵌套的 JSON 结构,就像播放列表可以有图片、视频一样,这个播放列表也可以有另一个嵌套的播放列表。
所以我希望得到输出,就像我从头到尾遍历最上面的播放列表一样,让它嵌套/子播放列表的项目只是落在最上面循环索引中的那个。
示例一:输入结构及其预期输出:
示例二:输入结构及其预期输出:
到目前为止我已经试过了
<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 即 get playlist 返回与示例二匹配的响应
解决方法
这是一个简单的递归解决方案
const myData = { contents: [{ name: 'image 1',type: 'file' },{ name: 'playlist 1',type: 'playlist',level: 1,contents: [{ name: 'image 3',{ name: 'playlist 101',level: 2,contents: [{ name: 'image 101',{ name: 'image 102',{ name: 'image 103',{ name: 'image 104',type: 'file' }] },{ name: 'image 4',{ name: 'image 2',{ name: 'playlist 2',contents: [{ name: 'image 5',{ name: 'image 6',{ 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 6' ]
// => [ 'image 1','image 4','image 7' ]
// => [ 'image 1','image 102','image 103','image 104','image 5' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
这是动态终止的更新版本
const myData = { contents: [{ name: 'image 1',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 },state)
);
const result = [];
do {
result.push(nextBatch());
} while (progress.cur !== progress.max);
return result;
};
console.log(iterate(myData));
/* => [
[ 'image 1','image 5' ],[ 'image 1','image 6' ],'image 7' ],'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",contents: [{name: "image 3",{name: "playlist 101",contents: [{name: "image 101",{name: "image 102",{name: "image 103",{name: "image 104",type: "file"}]},{name: "image 4",{name: "image 2",{name: "playlist 2",contents: [{name: "image 5",{name: "image 6",{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}
我们的输出只是一个由六个单独的 qux
调用组成的数组。但是 qux
只对 baz
进行了四次调用。这是它变得更有趣的地方。 baz
循环遍历四个函数,在每次调用时返回下一个函数的结果。这四个函数是() => 1
、foo
、() => 2
和bar
。 foo
和 bar
的结构与 foo
使用两个函数 () => 3
和 () => 4
和 bar
使用三个函数 {{1} }、() => 5
和 () => 6
。
所以上面的代码旨在将您的数据结构变成这样一个嵌套函数列表。 Central 是 () => 7
,它创建了我们的主函数(相当于上面的 makePlaylistFn
,通过用 baz
映射我们的值,它区分 makeHandler
和 "playlist"
输入,为前者递归调用 "file"
并为后者返回一个简单的函数。结果函数被传递给 makePlaylistFn
,后者将一组函数转换为在每次调用时循环调用它们的函数。
我们最后用 makeFn
来结束它,它调用 makePlayList
来生成那个根函数,并返回一个接受一个正整数的函数,返回那么多 makePlaylistFn
的数组调用main 函数,其中 n
是最外层播放列表的长度。
这仍然留下了一个有趣的问题。我们调用这个函数多少次?您上面的示例似乎表明您希望在看到每个值后停止。我相信我们可以解决这个问题,尽管这可能很棘手。
另一种可能性是运行此程序,直到您循环完所有内容并重新开始为止。这更易于处理,在各种嵌套播放列表上使用最少公倍数技术。但是对于您的简单案例需要 6 个调用,对于更复杂的案例则需要 12 个调用。如果你有兴趣,我可以尝试一下。
更新
您想知道如何在播放列表的值之间循环一次。替换 n
应该可以:
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,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)
}
的含义 例如,lcmAll
产生 lcmAll([12,15,20,35])
,这些整数的最小公倍数。它是作为我们最小公倍数函数 420
的简单简化而构建的,而后者又构建在最大公约数 lcm
函数之上。
使用它,我们在 gcd
中递归地找到每个图像/播放列表的 LCM。最后,我们重写 period
以使用它来确定要创建多少个数组。其余的将保持不变。您可以通过展开此代码段来查看它的实际效果:
makePlaylist
// utility functions
const gcd = (a,1)
// helper functions
const makeFn = (fns,contents}) =>
type == 'playlist'
? makePlaylistFn (contents)
: () => name
const period = (contents) => lcmAll (
[contents .length,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},f2)
}
// sample data
const data = {contents: [{name: "image 1",type: "file"}]}]}
// demo
console.log (makePlaylist (data))
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。