Javascript 用于生成树的递归函数 async-await
Javascript recursion function async-await for generating tree
我正在尝试生成这种 html 树的结构(例如,我使用了一家服装店)
<ul id="myUL">
<li><span class="caret">Kids cloths</span>
<ul class="nested">
<li>Boys</li>
<li>Girls</li>
<li><span class="caret">Dresses</span>
<ul class="nested">
<li><span class="caret">Maxi dresses</span>
<ul class="nested">
<li>dress 1</li>
<li>dress 2</li>
</ul>
</li>
<li><span class="caret">Mini dresses</span>
<ul class="nested">
<li>dress 3</li>
</ul>
</li>
</ul>
</li>
<li><span class="caret">Shirts</span>
<ul class="nested">
<li>shirt 1</li>
</ul>
</li>
</ul>
</li>
</ul>
**
可以有一个空类别(如“男孩”)和嵌套类别(女孩->连衣裙->长裙)。
最后我想访问某个产品并显示它的页面。
我得到的API,允许我发送一个类别名称,并得到它的所有子类别。
例如:getSubCategories(Kids cloths)-> 会给我:男孩,女孩。(都是类别)
getSubCategories(Girls)-> 会给我:连衣裙、衬衫。 (都是类别)
getSubCategories(Shirts)-> 将给我:衬衫 1.(产品)
我的代码是这样的:
function getSubCategories(CategoryName){
let fetchPromise = fetch(queryTextForAPI).then((response) => {
return response.json();
});
return fetchPromise;
}
async function build_category_tree_recursive(root_category){
let subcategories =await getSubCategories(root_category)
let data=subcategories.query.categorymembers;
if(data.length>0){
data.forEach(element=>{
if(element.type==="subcat"){//this is a category
(async () => {
let son_node=await build_category_tree_recursive(element.title);
//I'm not sure what to do here?
//How to generate a html code and concatenate it to "myUL"?
})();
}
else if(element.type==="page"){//it's a product
return element.title;
}
});
}
else{
//return the full htl that was generated?
}
}
getSubCategories("Kids cloths") 没有给我想要的 html。(我从代码中删除了生成 html 的代码,并保留了基本功能)
我试图连接 build_category_tree_recursive 调用的结果 - 但一切都搞砸了。
我还尝试构建一个节点class,并在每次调用中添加一个子节点,但它也没有用。
我在这里遗漏了一些东西,也许它与递归的理解有关,也许与异步等待的理解有关,也许对他们两者而言。
非常感谢您的帮助!
谢谢!
我将从将数据转换为单个对象开始。从那里我们可以做一个单独的步骤将其格式化为 HTML.
这个解决方案涉及到相互递归,简化了一些问题。我们的主函数 getStructure
调用您的 getSubCategories
函数,但也调用一个简单的 getAllSubCats
函数。那一个反过来调用相关子项的 getStructure
。
这是它的一种实现方式(带有 getSubCategories
的虚拟版本)
//-----------------------------------------------------------------------------------
// Main object fetching code
//-----------------------------------------------------------------------------------
const getAllSubCats = async (categories) =>
Promise .all (categories .map (
({type, title}) => type == 'subcat' ? getStructure (title) : {title}
))
const getStructure = async (title) => {
const children = await getSubCategories (title)
const subCategories = await getAllSubCats (children)
return {title, subCategories}
}
//-----------------------------------------------------------------------------------
// HTML formatting code
//-----------------------------------------------------------------------------------
const sp = (indent) => ' '.repeat(4 * indent)
const formatTree = ({title = '', subCategories = []}, indent) =>
subCategories.length
? `${sp(indent)}<li><span class="caret">${title}</span>
${sp(indent + 1)}<ul class="nested">
${subCategories.map(subcat => formatTree(subcat, indent + 2)).join('\n')}
${sp(indent + 1)}</ul>
${sp(indent)}</li>`
: `${sp(indent)}<li>${title}</li>`
const formatHtml = (tree) => `<ul id="myUL">\n${formatTree(tree, 1)}\n</ul>`
//-----------------------------------------------------------------------------------
// Demo code
//-----------------------------------------------------------------------------------
getStructure ('Kids clothes')
.then(logObj)
.then(formatHtml)
.then(console.log)
.as-console-wrapper {min-height: 100% !important; top: 0}
<script>
//-----------------------------------------------------------------------------------
// Fake implementation for demo
//-----------------------------------------------------------------------------------
const getSubCategories = ((cats) => async (name) =>
Promise.resolve(name in cats ? cats[name] : [])
)({
'Kids clothes': [{title: 'Boys', type: 'subcat'}, {title: 'Girls', type: 'subcat'}],
Boys: [],
Girls: [{title: 'Dresses', type: 'subcat'}, {title: 'Shirts', type: 'subcat'}],
Dresses: [{title: 'Maxi dresses', type: 'subcat'}, {title: 'Mini dresses', type: 'subcat'}],
'Maxi dresses': [{title: 'dress 1', type: 'page'}, {title: 'dress 2', type: 'page'}],
'Mini dresses': [{title: 'dress 3', type: 'page'}],
Shirts: [{title: 'shirt 1', type: 'page'}],
})
//-----------------------------------------------------------------------------------
// Helper function for demo
//-----------------------------------------------------------------------------------
const logObj = (obj) => console.log(JSON.stringify(obj, null, 4)) || obj
</script>
HTML 一代应该相当简单。唯一有点不寻常的是 indent
和 sp
以及 space 生成函数的使用。这在调试中非常有用,但最终,我可能会使用这些版本进行生产:
const formatTree = ({title = '', subCategories = []}) =>
subCategories.length
? `<li><span class="caret">${title}</span><ul class="nested">`
+ subCategories.map(subcat => formatTree(subcat)).join('')
+ `</ul></li>`
: `<li>${title}</li>`
const formatHtml = (tree) => `<ul id="myUL">${formatTree(tree, 1)}</ul>`
虽然这种分解为单独的对象生成和格式化步骤并不是必需的,但它通常会使此类问题变得更加简单。
请注意,这与您请求的输出不完全匹配。我认为您的版本存在问题,因为它没有将“连衣裙”嵌套在“女孩”下,即使您的示例响应是这样构建的。
此外,您可能需要根据实际响应的外观重新配置对象生成。我什至不尝试匹配,只是为了一些简单的东西。
最后,这里没有错误检查。我们假设任何请求都会成功。您可能需要在您的真实应用程序中进行实际错误检查。我把它留给你。
我正在尝试生成这种 html 树的结构(例如,我使用了一家服装店)
<ul id="myUL">
<li><span class="caret">Kids cloths</span>
<ul class="nested">
<li>Boys</li>
<li>Girls</li>
<li><span class="caret">Dresses</span>
<ul class="nested">
<li><span class="caret">Maxi dresses</span>
<ul class="nested">
<li>dress 1</li>
<li>dress 2</li>
</ul>
</li>
<li><span class="caret">Mini dresses</span>
<ul class="nested">
<li>dress 3</li>
</ul>
</li>
</ul>
</li>
<li><span class="caret">Shirts</span>
<ul class="nested">
<li>shirt 1</li>
</ul>
</li>
</ul>
</li>
</ul>
**
可以有一个空类别(如“男孩”)和嵌套类别(女孩->连衣裙->长裙)。 最后我想访问某个产品并显示它的页面。
我得到的API,允许我发送一个类别名称,并得到它的所有子类别。 例如:getSubCategories(Kids cloths)-> 会给我:男孩,女孩。(都是类别) getSubCategories(Girls)-> 会给我:连衣裙、衬衫。 (都是类别) getSubCategories(Shirts)-> 将给我:衬衫 1.(产品)
我的代码是这样的:
function getSubCategories(CategoryName){
let fetchPromise = fetch(queryTextForAPI).then((response) => {
return response.json();
});
return fetchPromise;
}
async function build_category_tree_recursive(root_category){
let subcategories =await getSubCategories(root_category)
let data=subcategories.query.categorymembers;
if(data.length>0){
data.forEach(element=>{
if(element.type==="subcat"){//this is a category
(async () => {
let son_node=await build_category_tree_recursive(element.title);
//I'm not sure what to do here?
//How to generate a html code and concatenate it to "myUL"?
})();
}
else if(element.type==="page"){//it's a product
return element.title;
}
});
}
else{
//return the full htl that was generated?
}
}
getSubCategories("Kids cloths") 没有给我想要的 html。(我从代码中删除了生成 html 的代码,并保留了基本功能)
我试图连接 build_category_tree_recursive 调用的结果 - 但一切都搞砸了。 我还尝试构建一个节点class,并在每次调用中添加一个子节点,但它也没有用。 我在这里遗漏了一些东西,也许它与递归的理解有关,也许与异步等待的理解有关,也许对他们两者而言。 非常感谢您的帮助! 谢谢!
我将从将数据转换为单个对象开始。从那里我们可以做一个单独的步骤将其格式化为 HTML.
这个解决方案涉及到相互递归,简化了一些问题。我们的主函数 getStructure
调用您的 getSubCategories
函数,但也调用一个简单的 getAllSubCats
函数。那一个反过来调用相关子项的 getStructure
。
这是它的一种实现方式(带有 getSubCategories
的虚拟版本)
//-----------------------------------------------------------------------------------
// Main object fetching code
//-----------------------------------------------------------------------------------
const getAllSubCats = async (categories) =>
Promise .all (categories .map (
({type, title}) => type == 'subcat' ? getStructure (title) : {title}
))
const getStructure = async (title) => {
const children = await getSubCategories (title)
const subCategories = await getAllSubCats (children)
return {title, subCategories}
}
//-----------------------------------------------------------------------------------
// HTML formatting code
//-----------------------------------------------------------------------------------
const sp = (indent) => ' '.repeat(4 * indent)
const formatTree = ({title = '', subCategories = []}, indent) =>
subCategories.length
? `${sp(indent)}<li><span class="caret">${title}</span>
${sp(indent + 1)}<ul class="nested">
${subCategories.map(subcat => formatTree(subcat, indent + 2)).join('\n')}
${sp(indent + 1)}</ul>
${sp(indent)}</li>`
: `${sp(indent)}<li>${title}</li>`
const formatHtml = (tree) => `<ul id="myUL">\n${formatTree(tree, 1)}\n</ul>`
//-----------------------------------------------------------------------------------
// Demo code
//-----------------------------------------------------------------------------------
getStructure ('Kids clothes')
.then(logObj)
.then(formatHtml)
.then(console.log)
.as-console-wrapper {min-height: 100% !important; top: 0}
<script>
//-----------------------------------------------------------------------------------
// Fake implementation for demo
//-----------------------------------------------------------------------------------
const getSubCategories = ((cats) => async (name) =>
Promise.resolve(name in cats ? cats[name] : [])
)({
'Kids clothes': [{title: 'Boys', type: 'subcat'}, {title: 'Girls', type: 'subcat'}],
Boys: [],
Girls: [{title: 'Dresses', type: 'subcat'}, {title: 'Shirts', type: 'subcat'}],
Dresses: [{title: 'Maxi dresses', type: 'subcat'}, {title: 'Mini dresses', type: 'subcat'}],
'Maxi dresses': [{title: 'dress 1', type: 'page'}, {title: 'dress 2', type: 'page'}],
'Mini dresses': [{title: 'dress 3', type: 'page'}],
Shirts: [{title: 'shirt 1', type: 'page'}],
})
//-----------------------------------------------------------------------------------
// Helper function for demo
//-----------------------------------------------------------------------------------
const logObj = (obj) => console.log(JSON.stringify(obj, null, 4)) || obj
</script>
HTML 一代应该相当简单。唯一有点不寻常的是 indent
和 sp
以及 space 生成函数的使用。这在调试中非常有用,但最终,我可能会使用这些版本进行生产:
const formatTree = ({title = '', subCategories = []}) =>
subCategories.length
? `<li><span class="caret">${title}</span><ul class="nested">`
+ subCategories.map(subcat => formatTree(subcat)).join('')
+ `</ul></li>`
: `<li>${title}</li>`
const formatHtml = (tree) => `<ul id="myUL">${formatTree(tree, 1)}</ul>`
虽然这种分解为单独的对象生成和格式化步骤并不是必需的,但它通常会使此类问题变得更加简单。
请注意,这与您请求的输出不完全匹配。我认为您的版本存在问题,因为它没有将“连衣裙”嵌套在“女孩”下,即使您的示例响应是这样构建的。
此外,您可能需要根据实际响应的外观重新配置对象生成。我什至不尝试匹配,只是为了一些简单的东西。
最后,这里没有错误检查。我们假设任何请求都会成功。您可能需要在您的真实应用程序中进行实际错误检查。我把它留给你。