用Meteor处理不规则嵌套对象/数组的最佳方法是什么

时间:2019-01-11 16:36:01

标签: javascript arrays node.js meteor nested

我很难找到一种优雅有效的方法来处理nodeJS中不规则的嵌套数组/对象。我所说的不规则是指可以具有1、2、3或更大维度的数组/对象。

例如,两种不同的情况:

第一种情况:

"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",
    ]
}

我想要的是能够提取所有内容,保留除不需要的所有内容之外的所有内容,最后将其清晰地打印在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中的嵌套方面不是问题,我只是在为算法苦苦挣扎。

1 个答案:

答案 0 :(得分:0)

进行任意深度的处理始终是一项艰巨的任务,尤其是当结构(如您的示例)在类型之间可能有所不同时。

要解决这种结构,您可以使用递归模板,该模板将迭代该结构并生成内容或继续扫描到最低级别的子级。

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

阅读模板代码中的注释,以了解其中发生的情况。

现在,由于预期的渲染逻辑的复杂性,我们需要一些辅助工具

  • 检测数据类型
  • 检测深度级别,增加深度级别
  • 将对象映射到可迭代
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>

example1example2是帮助者的地方,将数据传递到您的模板:

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

现在您可能想知道为什么我没有包括以下要求:

  

保留除我不想要的“选择”以外的所有内容

这是因为使用这样的模板,您已经可以完成一项复杂的任务:

  • 检测结构
  • 决定如何呈现内容
  • 呈现内容

将另一种责任压缩到这样的模板中会从字面上炸毁您的代码(在某些时候,我认为您的结构比本示例中的还要复杂)。

那您该怎么解决这个问题?

  • 确保仅将数据传递到将要呈现的模板
  • 让“父”模板处理(删除不必要的选择),这基本上是一项无需Blaze即可完成的任务,因为它主要由查找和过滤器组成。
  • 将任何内容保留在此模板之外,不仅仅呈现某些给定的数据。
  • 如果仍然无法解决这种封装方法,请对原始数据的嵌套性质进行修订。也许您可以将事物归一化以首先接收更线性的结构化数据。