微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用根节点项按顺序遍历嵌套的 JSON

如何解决如何使用根节点项按顺序遍历嵌套的 JSON

我有一个嵌套的 JSON 结构,就像播放列表可以有图片、视频一样,这个播放列表也可以有另一个嵌套的播放列表。

所以我希望得到输出,就像我从头到尾遍历最上面的播放列表一样,让它嵌套/子播放列表的项目只是落在最上面循环索引中的那个。

示例一:输入结构及其预期输出

enter image description here

示例二:输入结构及其预期输出

enter image description here

enter image description here

到目前为止我已经试过了

<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}

如果不存在 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",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 循环遍历四个函数,在每次调用时返回下一个函数的结果。这四个函数是() => 1foo() => 2barfoobar 的结构与 foo 使用两个函数 () => 3() => 4bar 使用三个函数 {{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 举报,一经查实,本站将立刻删除。