使用 Meteor 处理不规则嵌套 object/arrays 的最佳方法是什么
What is the best way to handle irregular nested object/arrays with Meteor
我很难找到一种优雅有效的方法来处理 nodeJS 中的不规则嵌套 arrays/objects。我所说的不规则是指 arrays/objects 可以有 1、2、3 或更多维度。
例如,2 种不同的情况:
第一种情况:
"head": {
"firstElement": {
"Choice1": [
"Some text",
"some text"
],
"Choice2": [
"Some text",
"Some text"
]
},
"SecondElement": [
"Some text",
"Some text"
]
}
第二种情况:
"head": {
"firstElement": [
"Some text",
"Some text"
],
"secondElement": [
"Some text",
]
}
我想要的是能够提取所有这些,保留除了 "Choices" 我不想要的所有内容,最后在 div.
中清楚地打印出来
预期:
<div>
<h1>FirstElement</h1>
<h2>Choice#</h2>
<p>Some text</p>
<p>Some text</p>
<h1>SecondElement</h1>
<p>Some text</p>
<p>Some text</p>
</div>
HTML 中的嵌套方面不是问题,我只是在努力解决算法问题。
处理任意层次的深度始终是一项具有挑战性的任务,尤其是当结构(如您的示例中)可能因类型而异时。
要处理这样的结构,您可以使用递归模板,它会迭代结构并生成内容或继续向下扫描到 children 的最低级别。
<template name="recursive">
{{#with data}}
{{#if isString this}}
<!--
Print string values directly as paragraph.
No recursion included.
-->
<p>{{this}}</p>
{{else if isArray this}}
<!--
If we have an array f values then iterate
and call a new template instance but don't
increase the level, since the values in the
array could also be just strings
-->
{{#each this}}
{{> recursive data=this level=level}}
{{/each}}
{{else if isObject this}}
<!--
Now with an object we need to transform it into
an iterable version, first (using the mapped helper).
We also need to increase the level of nesting using incLevel.
-->
{{#each map in (mapped this)}}
<!-- this is to keep the code more tidy and readable -->
{{> headline level=level title=map.key}}
{{> recursive data=map.value level=incLevel}}
{{/each}}
{{/if}}
{{/with}}
</template>
出于可读性的原因,我们可以提取逻辑以确定我们要将哪个标题呈现到自己的模板中:
<template name="headline">
{{#if level1 level}}
<h1>{{title}}</h1>
{{else if level2 level}}
<h2>{{title}}</h2>
{{else}}
{{level}}
{{/if}}
</template>
阅读模板代码中的注释,了解那里发生的事情。
现在,由于预期渲染逻辑的复杂性,我们需要一些助手来
- 检测数据类型
- 检测深度级别,增加深度级别
- 将Object映射到可迭代的
Template.recursive.helpers({
mapped (obj) {
return Object.keys(obj).map(key => ({ key, value: obj[ key ] }))
},
level() {
return Template.instance().data.level || 1
},
incLevel() {
const level = Template.instance().data.level
return level ? level + 1 : 2
},
isArray (element) {
return Array.isArray(element)
},
isString (element) {
return typeof element === 'string'
},
isObject (element) {
return typeof element === 'object'
}
})
请注意,级别以 1 开头,因为您的标题也以 1 开头。
说起头条,我们还需要头条模板的帮手:
Template.headline.helpers({
level1(lvl) {
return lvl === 1
},
level2(lvl) {
return lvl === 2
},
// ... up to 6
// then fallback to <strong> if lvl > 6
})
你可以这样调用模板(注意,我把它作为示例放在 <body>
):
<body>
{{> recursive data=example1}}
{{> recursive data=example2}}
</body>
其中 example1
和 example2
是助手,将数据传递给您的模板:
const example1 = {
'head': {
'firstElement': {
'Choice1': [
'Some text',
'some text'
],
'Choice2': [
'Some text',
'Some text'
]
},
'SecondElement': [
'Some text',
'Some text'
]
}
}
const example2 = {
'head': {
'firstElement': [
'Some text',
'Some text'
],
'secondElement': [
'Some text',
]
}
}
Template.body.helpers({
example1() {
return Object.values(example1)
},
example2() {
return Object.values(example2)
}
})
现在您可能想知道为什么我没有包括以下要求:
keep everything except the "Choices" I don't want
这是因为使用这样的模板,您已经完成了一项复杂的任务:
- 检测结构
- 决定如何呈现内容
- 渲染内容
将另一个职责压缩到这样的模板中实际上会破坏您的代码(在某些时候,我假设您的结构比本示例中的更复杂)。
那么你能做些什么来解决这个问题?
- 确保只将数据传递给将要呈现的模板
- 让 "parent" 模板处理(删除不需要的选择),这基本上是一个没有 Blaze 也可以完成的任务,因为它主要由查找和筛选组成。
- 除了呈现某些给定数据之外,请将任何内容排除在该模板之外。
- 如果仍然遇到问题或无法使用这种封装方法,请对原始数据的嵌套性质进行修改。也许您可以首先规范化事物以接收更线性的结构化数据。
我很难找到一种优雅有效的方法来处理 nodeJS 中的不规则嵌套 arrays/objects。我所说的不规则是指 arrays/objects 可以有 1、2、3 或更多维度。
例如,2 种不同的情况:
第一种情况:
"head": {
"firstElement": {
"Choice1": [
"Some text",
"some text"
],
"Choice2": [
"Some text",
"Some text"
]
},
"SecondElement": [
"Some text",
"Some text"
]
}
第二种情况:
"head": {
"firstElement": [
"Some text",
"Some text"
],
"secondElement": [
"Some text",
]
}
我想要的是能够提取所有这些,保留除了 "Choices" 我不想要的所有内容,最后在 div.
中清楚地打印出来预期:
<div>
<h1>FirstElement</h1>
<h2>Choice#</h2>
<p>Some text</p>
<p>Some text</p>
<h1>SecondElement</h1>
<p>Some text</p>
<p>Some text</p>
</div>
HTML 中的嵌套方面不是问题,我只是在努力解决算法问题。
处理任意层次的深度始终是一项具有挑战性的任务,尤其是当结构(如您的示例中)可能因类型而异时。
要处理这样的结构,您可以使用递归模板,它会迭代结构并生成内容或继续向下扫描到 children 的最低级别。
<template name="recursive">
{{#with data}}
{{#if isString this}}
<!--
Print string values directly as paragraph.
No recursion included.
-->
<p>{{this}}</p>
{{else if isArray this}}
<!--
If we have an array f values then iterate
and call a new template instance but don't
increase the level, since the values in the
array could also be just strings
-->
{{#each this}}
{{> recursive data=this level=level}}
{{/each}}
{{else if isObject this}}
<!--
Now with an object we need to transform it into
an iterable version, first (using the mapped helper).
We also need to increase the level of nesting using incLevel.
-->
{{#each map in (mapped this)}}
<!-- this is to keep the code more tidy and readable -->
{{> headline level=level title=map.key}}
{{> recursive data=map.value level=incLevel}}
{{/each}}
{{/if}}
{{/with}}
</template>
出于可读性的原因,我们可以提取逻辑以确定我们要将哪个标题呈现到自己的模板中:
<template name="headline">
{{#if level1 level}}
<h1>{{title}}</h1>
{{else if level2 level}}
<h2>{{title}}</h2>
{{else}}
{{level}}
{{/if}}
</template>
阅读模板代码中的注释,了解那里发生的事情。
现在,由于预期渲染逻辑的复杂性,我们需要一些助手来
- 检测数据类型
- 检测深度级别,增加深度级别
- 将Object映射到可迭代的
Template.recursive.helpers({
mapped (obj) {
return Object.keys(obj).map(key => ({ key, value: obj[ key ] }))
},
level() {
return Template.instance().data.level || 1
},
incLevel() {
const level = Template.instance().data.level
return level ? level + 1 : 2
},
isArray (element) {
return Array.isArray(element)
},
isString (element) {
return typeof element === 'string'
},
isObject (element) {
return typeof element === 'object'
}
})
请注意,级别以 1 开头,因为您的标题也以 1 开头。 说起头条,我们还需要头条模板的帮手:
Template.headline.helpers({
level1(lvl) {
return lvl === 1
},
level2(lvl) {
return lvl === 2
},
// ... up to 6
// then fallback to <strong> if lvl > 6
})
你可以这样调用模板(注意,我把它作为示例放在 <body>
):
<body>
{{> recursive data=example1}}
{{> recursive data=example2}}
</body>
其中 example1
和 example2
是助手,将数据传递给您的模板:
const example1 = {
'head': {
'firstElement': {
'Choice1': [
'Some text',
'some text'
],
'Choice2': [
'Some text',
'Some text'
]
},
'SecondElement': [
'Some text',
'Some text'
]
}
}
const example2 = {
'head': {
'firstElement': [
'Some text',
'Some text'
],
'secondElement': [
'Some text',
]
}
}
Template.body.helpers({
example1() {
return Object.values(example1)
},
example2() {
return Object.values(example2)
}
})
现在您可能想知道为什么我没有包括以下要求:
keep everything except the "Choices" I don't want
这是因为使用这样的模板,您已经完成了一项复杂的任务:
- 检测结构
- 决定如何呈现内容
- 渲染内容
将另一个职责压缩到这样的模板中实际上会破坏您的代码(在某些时候,我假设您的结构比本示例中的更复杂)。
那么你能做些什么来解决这个问题?
- 确保只将数据传递给将要呈现的模板
- 让 "parent" 模板处理(删除不需要的选择),这基本上是一个没有 Blaze 也可以完成的任务,因为它主要由查找和筛选组成。
- 除了呈现某些给定数据之外,请将任何内容排除在该模板之外。
- 如果仍然遇到问题或无法使用这种封装方法,请对原始数据的嵌套性质进行修改。也许您可以首先规范化事物以接收更线性的结构化数据。